Skip to content

fix(docker): register OpenRouter :variant slugs as OpenCode models#67

Closed
AbirAbbas wants to merge 2 commits into
mainfrom
fix/opencode-nitro-variant
Closed

fix(docker): register OpenRouter :variant slugs as OpenCode models#67
AbirAbbas wants to merge 2 commits into
mainfrom
fix/opencode-nitro-variant

Conversation

@AbirAbbas
Copy link
Copy Markdown
Collaborator

Investigation summary

Three symptoms the smoke test surfaced — all rooted in one bug:

  1. Builds with openrouter/minimax/minimax-m2.5:nitro fast-fail in 6 seconds with `ProviderModelNotFoundError`. OpenCode's openrouter binding rejects `:nitro` because it isn't in OpenCode's built-in model registry.
  2. "Last couple runs are using DeepSeek V3.1" — OpenCode's documented silent fallback for small_model when the configured HARNESS_MODEL isn't recognized. The Dockerfile comment on the line I'm replacing literally calls this out: "OpenCode auto-selects a small_model from whatever providers it finds keys for — landing on DeepSeek V3.1 in our environment, bypassing every env var the deployer set."
  3. PR-AF appearing to ignore `:nitro` (30 tps, slow lane) — same root cause: OpenCode's openrouter binding doesn't know about `:variant` suffixes. PR-AF "works" only because its pydantic `Field(default_factory=…)` cached an old kimi-k2.5 value at startup; logs show kimi-k2.5 across all recent calls, not minimax-nitro.

The 30-min hang we hit earlier with kimi-k2.6 was a separate failure mode (model registry knew about kimi-k2.6 but the OpenCode subprocess hung waiting on streamed chunks from OpenRouter). That's a different upstream issue. This PR addresses the `:nitro` rejection only.

Fix

Declare the slugs explicitly under `provider.openrouter.models[…]` in the Dockerfile-baked OpenCode config. OpenCode's config schema supports custom model declarations per provider; once a slug is in that list, OpenCode passes it through to OpenRouter verbatim and OpenRouter honors the routing suffix.

Declared:

  • `minimax/minimax-m2.5:nitro` — primary (Railway env)
  • `minimax/minimax-m2.5` — bare fallback for small_model resolution if env strips the suffix
  • `moonshotai/kimi-k2.6:nitro` — pre-declared in case kimi-k2.6 nitro is needed later

Limits are conservative defaults; OpenCode only needs the entry to exist for routing to fire. OpenRouter is the source of truth for actual context/output limits.

Also bumps the baked-in `ENV HARNESS_MODEL` to `openrouter/minimax/minimax-m2.5:nitro` so a fresh container without a Railway override matches the deployment reality. The previous `kimi-k2.6` default is what hung for 30 min in the first failed smoke test.

What this does NOT fix (separate follow-ups)

  • PR-AF needs the same Dockerfile change. Will follow up in a sibling PR.
  • PR-AF's pydantic `default_factory` env caching at startup. Even with this fix, an env change without a restart won't take effect on PR-AF. Separate refactor — change `config.py` to read env at request time, not at process start.
  • Hardcoded `model: str = "sonnet"` and `ai_provider: str = "claude"` defaults in SWE-AF reasoner kwargs (`pipeline.py`, `execution_agents.py`, `fast/init.py`, etc.). Don't fire in production (BuildConfig.resolved_models() always passes explicit values), but they're misleading in code review and would silently misbehave if any caller forgot to thread a model. Worth cleaning up but not blocking the smoke test.

Test plan

  • CI green
  • Railway redeploy of SWE-AF picks up the new Dockerfile (image rebuild required, not just env change)
  • Smoke test on a throwaway issue: `github buddy implement` → expect SWE-AF to reach Phase 1.5 approval checkpoint without `ProviderModelNotFoundError`
  • Confirm OpenRouter dashboard shows MiniMax M2.5 calls hitting fast-lane providers (not the standard slow-lane)
  • Verify no DeepSeek V3.1 calls appear in OpenRouter dashboard for these runs

🤖 Generated with Claude Code

AbirAbbas and others added 2 commits May 7, 2026 16:12
OpenCode's openrouter binding only knows model ids that match its
built-in registry — bare slugs like "minimax/minimax-m2.5". OpenRouter
routing variants (":nitro", ":floor", ":online", …) aren't in that
registry, so submitting "minimax/minimax-m2.5:nitro" produces:

    ProviderModelNotFoundError
      providerID: "openrouter"
      modelID:    "minimax/minimax-m2.5:nitro"

For main-model calls (per-call `-m` on `opencode run`) this fails the
reasoner outright with a fast 6-second error. For small_model (read
from {env:HARNESS_MODEL} in the config) OpenCode silently falls back to
DeepSeek V3.1, which is what the existing comment in this file warned
about. Net result: nitro routing was either dropped (silent fallback to
DeepSeek for small_model auxiliary calls) or failed the build outright
(main_model calls).

Fix: declare the slugs we use under provider.openrouter.models[…] so
OpenCode treats them as valid ids and passes them through to OpenRouter
verbatim. OpenRouter then honors the routing suffix and dispatches to
the fast-lane provider as intended.

Limits are conservative defaults — OpenCode only needs the entry to
exist for routing to fire; OpenRouter is the source of truth for actual
context/output limits.

Also bump the baked-in HARNESS_MODEL default to minimax/minimax-m2.5:nitro
so a fresh container with no Railway override matches what the deployment
is actually configured for. The previous kimi-k2.6 default is the one
that hung silently for 30 minutes in the smoke test that exposed this
whole chain of issues.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…encode.json

Heredoc-in-RUN works in BuildKit but trips Dockerfile linters and is
brittle when commands need to follow the heredoc body (the EOF line
breaks Dockerfile line-continuation, so a trailing chown ends up parsed
as a new instruction). PR-AF needs the same model-declaration block but
also has to chown the config directory afterwards — that's the case
that exposed the brittleness.

Move the JSON config to docker/opencode.json and COPY it. Same content,
no parser pitfalls, and the file is editable without grappling with
Dockerfile escaping.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@AbirAbbas
Copy link
Copy Markdown
Collaborator Author

Closing — overkill. The right answer is to drop the :nitro suffix in env (OpenCode's openrouter binding doesn't support OpenRouter routing variants). Hardcoding models in the Dockerfile to work around that just trades one form of hardcoding for another. Should have led with that limitation instead of putting up this PR.

For the smoke test: set SWE_DEFAULT_MODEL=openrouter/minimax/minimax-m2.5 and HARNESS_MODEL=openrouter/minimax/minimax-m2.5 (no :nitro) and the existing Dockerfile config works as-is.

@AbirAbbas AbirAbbas closed this May 7, 2026
@AbirAbbas AbirAbbas deleted the fix/opencode-nitro-variant branch May 7, 2026 20:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant