feat: add OpenCode Go as a third model router - #340
Conversation
|
@duncanmcqueen thanks this is a good idea. apologies but will you be able to (1) resign the CLA due to the jurisdiction clause update (2) rebase the PR branch on the latest changes? On (2), I have just implemented both the backend routing for model routing services Vercel AI Gateway and OpenRouter and frontend model selection UI flow. As such you can get your agent to reimplement the Opencode GO connection in the same format. |
|
Happily! Will do today! |
0b72bf2 to
78e63de
Compare
|
@duncanmcqueen thanks super useful. are you able to remove your defunct acct from this PR so the CLA check passes? |
Still waiting on reply from Github as the email address tied to that account is no longer active. |
78e63de to
4aec2c8
Compare
OpenCode 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>
4aec2c8 to
23b41c5
Compare
|
Done. Also rebased again (you move fast!) |
|
In Applied some other routing related changes unrelated to Opencode Go in
OpenCode Go catalog notes from review:
Validation completed: frontend ESLint/TypeScript and router settings tests, backend build and targeted tests, and Word add-in typechecking. |
|
@duncanmcqueen thank you for the contribution! |
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.
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.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 byROUTER_SLUGSrather 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/modelpairs, 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_REinroutes/user.ts,ROUTER_MODEL_IDinRouterSettingsSection.tsx) and the sharedresolveModelregex has a matching branch.Validation
Against
main@5b441de: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).tsc --noEmitclean;eslint0 errors;vitest run— 466 passed (baseline: 461).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 unmodifiedmainin my sandbox —Blob.text is not a functionandwindow.localStorageundefined, a local jsdom/env issue rather than a code change. Baseline is 12 failures, this branch is 14; the two extra are newuseSelectedModeltests blocked by that same missinglocalStorage, and CI should run them.