feat: add OpenCode Go as a third model router
From the PR description
Summary
Adds OpenCode Go - opencode.ai's low-cost subscription gateway for open coding models (GLM-5, Kimi K3, Qwen3.8 Max, MiniMax-M3, DeepSeek V4, ...) - as a third model router, alongside OpenRouter and the Vercel AI Gateway.
Rebased. This PR originally stacked on #339, which introduced a separate model-registry / OpenAI-compatible-provider foundation.
mainhas since landed its own router layer (llm/openrouter.tsserving both OpenRouter and Vercel,lib/routerModels.ts,user_router_models,RouterSettingsSection). Rather than land a second, competing provider architecture, the branch has been rewritten onto currentmainin that idiom. It no longer depends on #339 and is a single self-contained commit. It is also rebased onto the #357 design-system refactor, so the new group is declared once in the sharedModelToggleUIprimitive that both the web app and the Word add-in now render from.
What's added
Backend
lib/llm/openrouter.ts-opencode-gojoins theRouterProviderunion: base URLhttps://opencode.ai/zen/go/v1(overridable viaOPENCODE_GO_BASE_URL, like the other two), its own key lookup and label. OpenRouter'sHTTP-Referer/X-Titleattribution headers and itsreasoning: {...}payload stay OpenRouter-only; OpenCode Go usesreasoning_effortlike Vercel.lib/llm/models.ts-providerForModel/resolveModelacceptopencode-go/<model>;openCodeGoModelIdstrips the prefix.lib/userApiKeys.ts+ migration20260820_01- newopencode-goAPI-key provider (OPENCODE_API_KEYenv or encrypted per-user storage);schema.sqlupdated to match so the schema-drift check stays green.routes/models.ts-GET /models/opencode-goreturns the user's live catalog (key never leaves the server), deduplicated and sorted by label.routes/user.ts- the profile PATCH acceptsopenCodeGoModels, and the saved selection gates requests through the existinguser_router_modelschoke point, so a hand-crafted request still can't spend the operator's env key on an arbitrary gateway model.
Frontend + Word add-in
lib/mikeApi.ts-getOpenCodeGoModels(),opencode-goAPI-key provider,openCodeGoModelson the profile.- Settings → API Keys gains an OpenCode Go field; Settings → Routers gains an OpenCode Go model picker.
ModelToggle(web) and the add-in's mirrored catalog gain an "OpenCode Go" group, gated on the user's key.
Design note
A router's slug, model-id prefix and API-key provider name are one string. That invariant already held for openrouter/vercel, so this PR makes the code depend on it explicitly: routerForModelId, the selection lookups, the profile payload fields and the title-model fallback are now driven by ROUTER_SLUGS rather than hard-coded pairs. A fourth router should be mostly a list entry plus a label.
The one place the routers genuinely differ is catalog-id shape: OpenRouter and Vercel publish vendor/model pairs, OpenCode Go publishes bare names (glm-5). Requiring a slash would have rejected its entire catalog, so id validation is per-router on both sides (ROUTER_MODEL_ID_RE in routes/user.ts, ROUTER_MODEL_ID in RouterSettingsSection.tsx) and the shared resolveModel regex has a matching branch.
Validation
Against main @ 5b441de:
- backend:
tsc --noEmitclean;vitest run- 699 passed, 24 skipped (baseline onmain: 683 passed; +16 new tests covering the adapter, model ids, the catalog route, the per-router id shapes and the selection gate). - frontend:
tsc --noEmitclean;eslint0 errors;vitest run- 466 passed (baseline: 461). - word-addin:
typecheck:appandtypecheck:e2eclean; the cross-package catalog-parity and composer-availability drift guards extended to the new router and passing.
Three frontend files (mikeApi, useSelectedModel, ChatInput.modelSelection) fail identically on unmodified main in my sandbox - Blob.text is not a function and window.localStorage undefined, a local jsdom/env issue rather than a code change. Baseline is 12 failures, this branch is 14; the two extra are new useSelectedModel tests blocked by that same missing localStorage, and CI should run them.
Our analysis
Add OpenCode Go as a third model router — read the full analysis →
Think the analysis missed something the PR description covers?
Commits in this PR (4)
| SHA | Subject | Author | Date | |
|---|---|---|---|---|
23b41c5a | feat: add OpenCode Go as a third model router | duncanmcqueen | 2026-08-20 | ↗ GitHub |
commit bodyOpenCode Go (opencode.ai's low-cost hosted gateway for open coding models
- GLM-5, Kimi K3, Qwen3.8 Max, MiniMax-M3, DeepSeek V4) joins OpenRouter
and the Vercel AI Gateway as a router, reusing the existing router
plumbing end to end rather than adding a parallel one:
- opencode-go/<model> ids stream through the shared router adapter
against https://opencode.ai/zen/go/v1, with OPENCODE_GO_BASE_URL as the
usual proxy/test override. OpenRouter's attribution headers and
reasoning payload stay OpenRouter-only.
- New opencode-go user API key (OPENCODE_API_KEY env or encrypted
per-user storage) with a Settings > API Keys field and a migration
widening the user_api_keys provider CHECK.
- GET /models/opencode-go returns the live catalog with the key kept
server-side; Settings > Routers gains an OpenCode Go picker, and the
saved selection gates requests through the same user_router_models
choke point that stops a hand-crafted request from spending the
operator's env key on an arbitrary gateway model.
- Both model pickers and the Word add-in gain an "OpenCode Go" group.
Router slugs, model-id prefixes and API-key provider names are one string
per router, so routerForModelId, the selection lookups and the profile
payload are now driven by ROUTER_SLUGS instead of a hard-coded pair. The
one place the routers genuinely differ is catalog-id shape: OpenCode Go
publishes bare model names ("glm-5"), so id validation is per-router.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
| ||||
da8ed0e5 | fix(llm): gate OpenCode Go models by protocol | willchen96 | 2026-08-21 | ↗ GitHub |
3ec6d060 | feat(llm): support OpenCode Go Messages models | willchen96 | 2026-08-21 | ↗ GitHub |
0f006e2c | refactor(settings): clarify bring your own keys navigation | willchen96 | 2026-08-21 | ↗ GitHub |
Capture this PR into my fork
Download a Markdown prompt that tells Claude how to port every
commit in this PR into your working tree. Run it via
claude -p < capture-pull-340.md from
inside the repo you want the changes in.