Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,13 +161,13 @@ Zen's provider-level SDK is `@ai-sdk/openai-compatible`. models.dev names a **di

| models.dev `provider.npm` | models | protocol | served from |
| :-- | --: | :-- | :-- |
| _(absent)_ | 24 | OpenAI Chat Completions | the route you configured |
| _(absent)_ | 25 | OpenAI Chat Completions | the route you configured |
| `@ai-sdk/openai` | 31 | OpenAI Responses | `opencode-responses` |
| `@ai-sdk/anthropic` | 17 | Anthropic Messages | `opencode-anthropic` |
| `@ai-sdk/google` | 8 | Google Generative AI | `opencode-google` |
| `@ai-sdk/mistral` | 1 | _(a dialect this plugin does not serve)_ | **not offered** |

Counts are the **81** active `opencode` models in `models.opencode.ai` as of 2026-11-03 — everything else the vendor lists is deprecated or retired, and the patch ships only what the gateway still serves. The same 81 are bundled offline in `src/catalog-data.ts`, so every one of them routes correctly before the first catalog refresh; regenerate that file with `pnpm run catalog:shim -- --write` (without `--write` it only checks).
Counts are the **82** active `opencode` models in `models.opencode.ai` as of 2026-11-04 — everything else the vendor lists is deprecated or retired, and the patch ships only what the gateway still serves. The same 82 are bundled offline in `src/catalog-data.ts`, so every one of them routes correctly before the first catalog refresh; regenerate that file with `pnpm run catalog:shim -- --write` (without `--write` it only checks).

The patch **keeps those two routes out of both the model picker and _Settings → Models_**. Three properties hold today, and one does not yet:

Expand Down
2 changes: 1 addition & 1 deletion README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ Zen 的 provider 级 SDK 是 `@ai-sdk/openai-compatible`。models.dev **只在
| `@ai-sdk/google` | 8 | Google Generative AI | `opencode-google` |
| `@ai-sdk/mistral` | 1 | _(本插件不服务的方言)_ | **不提供** |

数量为 `models.opencode.ai` 中 `opencode` 的 81 个**在用**模型,统计于 2026-11-03 —— 其余已废弃或下架,补丁只内置网关仍在服务的部分。同样这 81 个模型离线内置在 `src/catalog-data.ts`,因此在第一次 catalog 刷新之前它们每一个路由都是正确的;用 `pnpm run catalog:shim -- --write` 重新生成该文件(不加 `--write` 时只做检查)。
数量为 `models.opencode.ai` 中 `opencode` 的 82 个**在用**模型,统计于 2026-11-04 —— 其余已废弃或下架,补丁只内置网关仍在服务的部分。同样这 82 个模型离线内置在 `src/catalog-data.ts`,因此在第一次 catalog 刷新之前它们每一个路由都是正确的;用 `pnpm run catalog:shim -- --write` 重新生成该文件(不加 `--write` 时只做检查)。

插件把这两条内部路由同时挡在模型选择器和 _Settings → Models_ 之外。**三条性质今天就成立,还有一条尚未成立:**

Expand Down
10 changes: 5 additions & 5 deletions docs/protocol-routing-and-merge.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ OpenCode serves its models on **four different API shapes**, and which one a mod

| API shape | Wire endpoint | Which models | Signal in the catalog |
| :-- | :-- | :-- | :-- |
| OpenAI **Chat Completions** | `POST /zen/v1/chat/completions` | **the default**: 24 of 81 | the model names **no** SDK |
| OpenAI **Responses** | `POST /zen/v1/responses` | 31 of 81 | `@ai-sdk/openai` |
| Anthropic **Messages** | `POST /zen/v1/messages` | 17 of 81 | `@ai-sdk/anthropic` |
| Google **Generative AI** | `POST /zen/v1/models/<id>:streamGenerateContent` | 8 of 81 | `@ai-sdk/google` |
| Mistral **Conversations** | `POST /zen/v1/chat/completions` | 1 of 81 | `@ai-sdk/mistral` |
| OpenAI **Chat Completions** | `POST /zen/v1/chat/completions` | **the default**: 25 of 82 | the model names **no** SDK |
| OpenAI **Responses** | `POST /zen/v1/responses` | 31 of 82 | `@ai-sdk/openai` |
| Anthropic **Messages** | `POST /zen/v1/messages` | 17 of 82 | `@ai-sdk/anthropic` |
| Google **Generative AI** | `POST /zen/v1/models/<id>:streamGenerateContent` | 8 of 82 | `@ai-sdk/google` |
| Mistral **Conversations** | `POST /zen/v1/chat/completions` | 1 of 82 | `@ai-sdk/mistral` |

Counts are the shipped catalog. They are asserted against this table by `catalog.test.ts`, because a count written into prose does not regenerate with the data it counts.

Expand Down
6 changes: 3 additions & 3 deletions docs/redirect-planes.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ A **plane** is one gateway base URL with its own catalog and its own credential.

| Plane | Route id | Base URL | Credential | Models |
| :-- | :-- | :-- | :-- | --: |
| Zen (pay-as-you-go, free tier) | `opencode` | the `endpoint` family's | `OPENCODE_API_KEY` | 81 served / 131 listed |
| Zen (pay-as-you-go, free tier) | `opencode` | the `endpoint` family's | `OPENCODE_API_KEY` | 82 served / 132 listed |
| Go (subscription) | `opencode-go` | the `endpoint` family's | `OPENCODE_GO_API_KEY` | 30 served / 37 listed |

Counts as of 2026-11-03 (the shipped shim, regenerated in #132 and refreshed for `pulse-alpha-free`); the document's 2026-10-09 stamp is for its measurements.
Expand All @@ -27,13 +27,13 @@ A **shape** is one wire protocol, named by the SDK a model declares in the catal

| SDK in the catalog | Protocol | Zen | Go |
| :------------------ | :---------------------- | --: | ----: |
| _(none)_ | `openai-completions` | 24 | 19 |
| _(none)_ | `openai-completions` | 25 | 19 |
| `@ai-sdk/openai` | `openai-responses` | 31 | **5** |
| `@ai-sdk/anthropic` | `anthropic-messages` | 17 | **5** |
| `@ai-sdk/mistral` | `mistral-conversations` | 1 | — |
| `@ai-sdk/google` | `google-generative-ai` | 8 | **1** |

**Absence is the signal, and it is a real one.** Both planes set a provider-level `npm` of `@ai-sdk/openai-compatible` in `models.dev`, and **no model declares it explicitly**: 0 of 81 on Zen, 0 of 30 on Go. A model that speaks the completions shape simply omits `provider.npm` and inherits it. So "no SDK" does not mean "unknown"; it means _the provider default_, which is the protocol the route was itself registered with.
**Absence is the signal, and it is a real one.** Both planes set a provider-level `npm` of `@ai-sdk/openai-compatible` in `models.dev`, and **no model declares it explicitly**: 0 of 82 on Zen, 0 of 30 on Go. A model that speaks the completions shape simply omits `provider.npm` and inherits it. So "no SDK" does not mean "unknown"; it means _the provider default_, which is the protocol the route was itself registered with.

That is why the servability check treats `undefined` as servable, and why `@ai-sdk/openai-compatible` is deliberately **not** in `PROTOCOL_FOR_SDK`: listing it would record the default as though it were an override. The coverage guard still covers the case where a model declares it explicitly. None do today, and if one did, the test would name it rather than drop the model.

Expand Down
22 changes: 21 additions & 1 deletion src/catalog-data.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
/**
* Static OpenCode Go & Zen model catalog data.
*
Expand Down Expand Up @@ -161,7 +161,7 @@
id: "deepseek-v4-pro",
input_modalities: ["text"],
max_output_tokens: 384_000,
name: "DeepSeek V4 Pro (New)",
name: "DeepSeek V4 Pro",
reasoning: true,
reasoning_options: [{ type: "effort", values: ["high", "max"] }],
},
Expand Down Expand Up @@ -869,6 +869,26 @@
{ type: "effort", values: ["low", "medium", "high", "xhigh", "max"] },
],
},
{
context_window: 1_048_576,
cost: {
cache_read: 0,
input: 0,
output: 0,
},
id: "crouton-alpha-free",
input_modalities: ["text", "image"],
is_free: true,
max_output_tokens: 270_000,
name: "Crouton Alpha Free",
reasoning: true,
reasoning_options: [
{
type: "effort",
values: ["minimal", "low", "medium", "high", "xhigh", "max"],
},
],
},
{
context_window: 1_000_000,
cost: {
Expand Down
4 changes: 2 additions & 2 deletions test/catalog.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,12 +198,12 @@ describe("OpenCode Model Catalog & Enrichment", () => {
// replacing the whole set — so these counts are the cold-start picker, and a
// silent drop here is a model nobody can pick. Regenerate with
// `scripts/regenerate-catalog-shim.ts` rather than editing by hand.
expect(OPENCODE_ZEN_CATALOG.length).toBe(81);
expect(OPENCODE_ZEN_CATALOG.length).toBe(82);
const freeModels = OPENCODE_ZEN_CATALOG.filter((m) => m.is_free === true);
// Only the free tiers the registry still lists as active. The count is the
// `is_free` flag the shim carries, NOT the `-free` suffix: `big-pickle` is
// flagged and is not suffixed, so a suffix rule would have missed it.
expect(freeModels.length).toBe(9);
expect(freeModels.length).toBe(10);

const ids = new Set(OPENCODE_ZEN_CATALOG.map((m) => m.id));
expect(ids.has("beam-free")).toBe(true);
Expand Down
Loading