From 938762b8380d1c4b7ac935a97491fb10bb804078 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 4 Nov 2026 12:27:10 +0000 Subject: [PATCH 1/2] chore: refresh the generated catalog and limits tables --- src/catalog-data.ts | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/catalog-data.ts b/src/catalog-data.ts index d611daf..30739dd 100644 --- a/src/catalog-data.ts +++ b/src/catalog-data.ts @@ -161,7 +161,7 @@ export const OPENCODE_GO_CATALOG: readonly CatalogModelSpec[] = [ 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"] }], }, @@ -869,6 +869,26 @@ export const OPENCODE_ZEN_CATALOG: readonly CatalogModelSpec[] = [ { 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: { From 1d173449061e59b1b60138fa245765ef9e6634b5 Mon Sep 17 00:00:00 2001 From: Leo Date: Thu, 5 Nov 2026 03:23:46 +0800 Subject: [PATCH 2/2] chore(catalog): carry the refresh into the docs and the counts it changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The nightly regenerated `src/catalog-data.ts` for a vendor move — `DeepSeek V4 Pro (New)` renamed to `DeepSeek V4 Pro`, and one new free model, `crouton-alpha-free` — and the branch was opened with the generated file only, so six tests failed on it: the documents that COUNT the catalog, and the one place the count is written down on purpose. - Zen: **81 → 82** active models, `(absent)` SDK bucket **24 → 25**, free tiers **9 → 10**; - `README.md` and `README.zh-CN.md`: the routing table's `(absent)` row and both prose totals (and the measurement date, which moves with them); - `docs/protocol-routing-and-merge.md`: every `N of 81` row; - `docs/redirect-planes.md`: the served/listed cell, the `0 of 81` sentence and the plane-by-plane `(none)` cell; - `test/catalog.test.ts`: the two deliberate constants. They stay literal on purpose — a silent drop is a model nobody can pick, so a change has to be seen. `pnpm run catalog:shim` reports the shim current and `pnpm run limits:shim` reports the allowance table up to date, so the generated files and the vendor agree; the suite is 1332 green. --- README.md | 4 ++-- README.zh-CN.md | 2 +- docs/protocol-routing-and-merge.md | 10 +++++----- docs/redirect-planes.md | 6 +++--- test/catalog.test.ts | 4 ++-- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 3618a04..8e9435a 100644 --- a/README.md +++ b/README.md @@ -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: diff --git a/README.zh-CN.md b/README.zh-CN.md index c2ed8dd..3ebf846 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -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_ 之外。**三条性质今天就成立,还有一条尚未成立:** diff --git a/docs/protocol-routing-and-merge.md b/docs/protocol-routing-and-merge.md index 5a366b3..d2ff35e 100644 --- a/docs/protocol-routing-and-merge.md +++ b/docs/protocol-routing-and-merge.md @@ -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/: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/: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. diff --git a/docs/redirect-planes.md b/docs/redirect-planes.md index fb36278..97e7868 100644 --- a/docs/redirect-planes.md +++ b/docs/redirect-planes.md @@ -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. @@ -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. diff --git a/test/catalog.test.ts b/test/catalog.test.ts index d140d56..a7f6b68 100644 --- a/test/catalog.test.ts +++ b/test/catalog.test.ts @@ -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);