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/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: { 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);