Route OpenCode explicit model overrides through the AI Gateway#170
Merged
Conversation
…teway An explicit --model like anthropic/claude-sonnet-5 was passed to the OpenCode CLI verbatim, which reads the first segment as its provider id. The generated opencode.json only configures the vercel (AI Gateway) provider, so every explicit-model run using a canonical gateway id died at session start with "Unexpected server error" and a null observedModel. - resolveOpenCodeModel (host-side): prefix vercel/ unless the caller already targets vercel/... or a configured extraProviders key. - Threaded to the runner via runnerExtra -> input.extra.cliModel, mirroring codex's host-computed model (the judge path ships extra but not agentOptions, so the resolution must happen host-side). - normalizeObservedModel (runner-side): un-apply exactly that prefix on the observation, so observedModel === requestedModel holds for canonical gateway ids and a gateway substitution still surfaces as a clean gateway id. Native-default observations are untouched, and the documented vercel/-prefixed form still passes through verbatim. - README: the OpenCode model format section now documents both forms (it previously stated unprefixed ids fail with provider-not-found).
gaojude
approved these changes
Jul 6, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
An explicit
modeloverride likeanthropic/claude-sonnet-5was passed to the OpenCode CLI verbatim (--model anthropic/claude-sonnet-5). OpenCode reads the first segment as its provider id, and the generatedopencode.jsononly configures thevercel(AI Gateway) provider — so every explicit-model run using a canonical gateway id died at session start:{"type":"error","error":{"name":"UnknownError","data":{"message":"Unexpected server error. Check server logs for details."}}}with
observedModel: nullon every task. The form OpenCode actually routes through the gateway isvercel/anthropic/claude-sonnet-5— consistent with the package's owndefaultModelshape and the README's OpenCode section. claude-code and codex were unaffected because their adapters route via gateway env/config (ANTHROPIC_BASE_URL,model_provider = \"vercel\").Fix
Mirrors codex's host-computed model handling (
openai/prefixing):resolveOpenCodeModel(host-side,opencode/agent.ts) — prefixesvercel/unless the caller already targetsvercel/...or a key configured viaagentOptions.extraProviders(the unreleased-models escape hatch). Host-side because only the host knowsextraProviders.runnerExtra()— threads the resolved id to the sandbox runner asinput.extra.cliModel. The judge path (eval-helper.mjs) shipsextrabut notagentOptions, so the resolution must happen host-side. The runner deliberately has no fallback toinput.model— verbatim pass-through is the exact mis-route this fixes, and both runner entry points always shipextra.normalizeObservedModel(runner-side,opencode/run.mjs) — un-applies exactly the prefix the host added, soobservedModel === requestedModelholds for canonical gateway ids, and a gateway substitution still surfaces as a clean gateway id (anthropic/claude-haiku-4, notvercel/anthropic/...).Compatibility
vercel/{provider}/{model}form passes through verbatim — zero change for existing consumers following the docs.vercel/google/gemini-3-pro-preview) pass through unchanged — no published-data drift.extraProvidersrequests are passed verbatim, preserving that workflow.resolveOpenCodeModelhas the same internal/test-only visibility asgenerateOpenCodeConfig) →patchchangeset.Testing
vercel/...ids,extraProvidersverbatim + gateway ids still prefixed alongside them, bare ids (no/), no-verbatim-fallback in arg building, native-default arg parity, observed-model round-trip, gateway-substitution reporting, and native-default observation pass-through.copy-runnersbuild verified.