diff --git a/changelog.d/fixes/6998-opencode-oc-free-tier-catalog.md b/changelog.d/fixes/6998-opencode-oc-free-tier-catalog.md new file mode 100644 index 0000000000..e5a3f5189b --- /dev/null +++ b/changelog.d/fixes/6998-opencode-oc-free-tier-catalog.md @@ -0,0 +1 @@ +- fix(providers): refresh OpenCode (`oc`) free-tier model catalog — 6 delisted IDs replaced with the 4 currently-live free models (#6998) diff --git a/open-sse/config/providers/registry/opencode/index.ts b/open-sse/config/providers/registry/opencode/index.ts index 5f3779d72a..4aaa6ea046 100644 --- a/open-sse/config/providers/registry/opencode/index.ts +++ b/open-sse/config/providers/registry/opencode/index.ts @@ -23,29 +23,15 @@ export const opencodeProvider: RegistryEntry = { interleavedField: "reasoning_content", }, { id: "deepseek-v4-flash-free", name: "DeepSeek V4 Flash Free", supportsReasoning: true }, - // #3110: MiniMax M3 free tier via OpenCode - // #3328: MiniMax M3 is multimodal (verified: describes base64 images via the - // opencode upstream) — flag it so vision requests aren't gated/stripped. - { - id: "minimax-m3-free", - name: "MiniMax M3 Free", - contextLength: 1048576, - supportsVision: true, - }, - { id: "minimax-m2.5-free", name: "MiniMax M2.5 Free", contextLength: 204800 }, - { id: "ling-2.6-1t-free", name: "Ling 2.6 Free", contextLength: 262000 }, - { - id: "trinity-large-preview-free", - name: "Trinity Large Preview Free", - contextLength: 131000, - }, - { id: "nemotron-3-super-free", name: "Nemotron 3 Super Free", contextLength: 1000000 }, - { - id: "qwen3.6-plus-free", - name: "Qwen3.6 Plus Free", - targetFormat: "claude", - supportsVision: false, - contextLength: 200000, - }, + // #6998: 2026-07-14 refresh — the upstream free tier rotated its lineup; + // minimax-m3-free, minimax-m2.5-free, ling-2.6-1t-free, + // trinity-large-preview-free, nemotron-3-super-free and qwen3.6-plus-free + // were delisted (401 "Model X is not supported") and replaced by the 4 + // entries below, confirmed live against + // https://opencode.ai/zen/v1/chat/completions. + { id: "mimo-v2.5-free", name: "MiMo V2.5 Free", contextLength: 131000 }, + { id: "hy3-free", name: "HY3 Free", contextLength: 131000 }, + { id: "nemotron-3-ultra-free", name: "Nemotron 3 Ultra Free", contextLength: 1000000 }, + { id: "north-mini-code-free", name: "North Mini Code Free", contextLength: 131000 }, ], }; diff --git a/tests/unit/minimax-m3-model-registry.test.ts b/tests/unit/minimax-m3-model-registry.test.ts index 820cbc77ca..2aae2b3a69 100644 --- a/tests/unit/minimax-m3-model-registry.test.ts +++ b/tests/unit/minimax-m3-model-registry.test.ts @@ -29,13 +29,11 @@ describe("MiniMax M3 model registration (#3110)", () => { assert.equal(m3.contextLength, 1_048_576); }); - it("opencode provider has minimax-m3-free with 1M context", () => { + it("opencode provider does NOT list minimax-m3-free (#6998 — delisted upstream, 401)", () => { const entry = REGISTRY.opencode; assert.ok(entry, "opencode registry entry must exist"); const m3 = entry.models.find((m) => m.id === "minimax-m3-free"); - assert.ok(m3, "minimax-m3-free must be in opencode models"); - assert.equal(m3.name, "MiniMax M3 Free"); - assert.equal(m3.contextLength, 1_048_576); + assert.equal(m3, undefined, "minimax-m3-free was delisted from OpenCode Zen's free tier (#6998)"); }); it("opencode-go provider has minimax-m3 with Claude targetFormat", () => { diff --git a/tests/unit/opencode-free-tier-catalog-stale-6998.test.ts b/tests/unit/opencode-free-tier-catalog-stale-6998.test.ts new file mode 100644 index 0000000000..c1d3eeae09 --- /dev/null +++ b/tests/unit/opencode-free-tier-catalog-stale-6998.test.ts @@ -0,0 +1,40 @@ +import test from "node:test"; +import assert from "node:assert/strict"; + +const { opencodeProvider } = await import( + "../../open-sse/config/providers/registry/opencode/index.ts" +); + +function modelIds(): string[] { + return (opencodeProvider.models ?? []).map((m) => m.id); +} + +const DELISTED_FREE_MODELS = [ + "minimax-m3-free", + "minimax-m2.5-free", + "ling-2.6-1t-free", + "trinity-large-preview-free", + "nemotron-3-super-free", + "qwen3.6-plus-free", +]; + +const LIVE_FREE_MODELS_MISSING_FROM_CATALOG = [ + "mimo-v2.5-free", + "hy3-free", + "nemotron-3-ultra-free", + "north-mini-code-free", +]; + +test("issue #6998: oc registry does not advertise delisted free-tier models", () => { + const ids = modelIds(); + for (const delisted of DELISTED_FREE_MODELS) { + assert.ok(!ids.includes(delisted), `oc registry still advertises delisted upstream model "${delisted}"`); + } +}); + +test("issue #6998: oc registry advertises the current live free-tier models", () => { + const ids = modelIds(); + for (const live of LIVE_FREE_MODELS_MISSING_FROM_CATALOG) { + assert.ok(ids.includes(live), `oc registry is missing live upstream free-tier model "${live}"`); + } +}); diff --git a/tests/unit/provider-registry-qwen-vision.test.ts b/tests/unit/provider-registry-qwen-vision.test.ts index 9382027aed..1b2ddaa3f2 100644 --- a/tests/unit/provider-registry-qwen-vision.test.ts +++ b/tests/unit/provider-registry-qwen-vision.test.ts @@ -63,16 +63,17 @@ test("#2822 opencode-go/qwen3.6-plus deve ter supportsVision !== true", () => { ); }); -// #3328 — o oposto do #2822: MiniMax M3 (opencode) É multimodal (verificado -// empiricamente: descreve imagens base64 via o upstream opencode). Deve ter -// supportsVision: true para não ser barrado/strippado em requests com imagem. -test("#3328 opencode/minimax-m3-free deve ter supportsVision: true", () => { +// #3328 — o oposto do #2822: MiniMax M3 (opencode) era multimodal (verificado +// empiricamente: descrevia imagens base64 via o upstream opencode). #6998: +// minimax-m3-free foi deslistado do free tier da OpenCode Zen (401 "not +// supported") em 2026-07-14 e removido do catálogo estático — este teste +// agora confirma a remoção. +test("#6998 opencode/minimax-m3-free não deve mais estar registrado (deslistado upstream)", () => { const model = getModel("opencode", "minimax-m3-free"); - assert.ok(model, "minimax-m3-free deve estar registrado em opencode"); - assert.strictEqual( - model.supportsVision, - true, - "opencode/minimax-m3-free é multimodal — supportsVision deve ser true" + assert.equal( + model, + undefined, + "opencode/minimax-m3-free foi deslistado do free tier da OpenCode Zen (#6998)" ); });