Skip to content

feat: add OpenCode Go as a third model router - #340

Merged
willchen96 merged 4 commits into
open-legal-products:mainfrom
duncanmcqueen:feat/opencode-go
Aug 21, 2026
Merged

feat: add OpenCode Go as a third model router#340
willchen96 merged 4 commits into
open-legal-products:mainfrom
duncanmcqueen:feat/opencode-go

Conversation

@duncanmcqueen

@duncanmcqueen duncanmcqueen commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

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. main has since landed its own router layer (llm/openrouter.ts serving 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 current main in 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 shared ModelToggleUI primitive that both the web app and the Word add-in now render from.

What's added

Backend

  • lib/llm/openrouter.tsopencode-go joins the RouterProvider union: base URL https://opencode.ai/zen/go/v1 (overridable via OPENCODE_GO_BASE_URL, like the other two), its own key lookup and label. OpenRouter's HTTP-Referer/X-Title attribution headers and its reasoning: {…} payload stay OpenRouter-only; OpenCode Go uses reasoning_effort like Vercel.
  • lib/llm/models.tsproviderForModel / resolveModel accept opencode-go/<model>; openCodeGoModelId strips the prefix.
  • lib/userApiKeys.ts + migration 20260820_01 — new opencode-go API-key provider (OPENCODE_API_KEY env or encrypted per-user storage); schema.sql updated to match so the schema-drift check stays green.
  • routes/models.tsGET /models/opencode-go returns the user's live catalog (key never leaves the server), deduplicated and sorted by label.
  • routes/user.ts — the profile PATCH accepts openCodeGoModels, and the saved selection gates requests through the existing user_router_models choke 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.tsgetOpenCodeGoModels(), opencode-go API-key provider, openCodeGoModels on 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 --noEmit clean; vitest run699 passed, 24 skipped (baseline on main: 683 passed; +16 new tests covering the adapter, model ids, the catalog route, the per-router id shapes and the selection gate).
  • frontend: tsc --noEmit clean; eslint 0 errors; vitest run466 passed (baseline: 461).
  • word-addin: typecheck:app and typecheck:e2e clean; 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.

@CLAassistant

CLAassistant commented Aug 15, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@willchen96

Copy link
Copy Markdown
Collaborator

@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.

@duncanmcqueen

Copy link
Copy Markdown
Contributor Author

Happily! Will do today!

@duncanmcqueen duncanmcqueen changed the title feat: add OpenCode Go as a model provider with dynamic catalog feat: add OpenCode Go as a third model router Aug 20, 2026
@willchen96

Copy link
Copy Markdown
Collaborator

@duncanmcqueen thanks super useful. are you able to remove your defunct acct from this PR so the CLA check passes?

@duncanmcqueen

Copy link
Copy Markdown
Contributor Author

@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.

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>
@duncanmcqueen

Copy link
Copy Markdown
Contributor Author

Done. Also rebased again (you move fast!)

@willchen96

willchen96 commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

In da8ed0e I routed certain models which had Anthropic compatible endpoints to claude.ts.

Applied some other routing related changes unrelated to Opencode Go in0f006e2c:

  • Renamed the settings navigation item from BYOK to Bring Your Own Keys.
  • Moved the frontend page from /settings/api-keys to /settings/byok and updated the missing-key popup, Word add-in banner, and end-to-end coverage to use the new URL. The backend /user/api-keys API remains unchanged.
  • Updated user-facing router guidance to say Settings → Bring Your Own Keys → Routers.
  • Removed the repeated per-provider model captions and added one shared explanation beneath the Routers heading.

OpenCode Go catalog notes from review:

  • Mike fetches the catalog from OpenCode Go’s /v1/models endpoint.
  • The live response supplies model IDs but no separate display names or pricing fields, so Mike derives readable names from the IDs and cannot display costs from this endpoint.
  • A configured OpenCode Go key alone does not add the provider to the model toggle; at least one OpenCode Go model must also be saved under Routers.

Validation completed: frontend ESLint/TypeScript and router settings tests, backend build and targeted tests, and Word add-in typechecking.

@willchen96
willchen96 merged commit 2ae4ca7 into open-legal-products:main Aug 21, 2026
12 checks passed
@willchen96

Copy link
Copy Markdown
Collaborator

@duncanmcqueen thank you for the contribution!

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.

3 participants