diff --git a/README.md b/README.md index de1dae2221..5f6976fe41 100644 --- a/README.md +++ b/README.md @@ -204,7 +204,7 @@ Advanced and source-build guides: | Codex | `/provider` | Uses existing Codex CLI auth, OpenClaude secure storage, or env credentials | | Gitlawb Opengateway | Startup default, `/provider`, or env vars | Smart gateway at `https://opengateway.gitlawb.com/v1`; requires an API key from https://gitlawb.com/opengateway/keys and routes Xiaomi MiMo and GMI Cloud partner models by `OPENAI_MODEL` | | OpenCode Zen | `/provider` or env vars | Pay-as-you-go AI gateway (48 models); uses `OPENCODE_API_KEY` via `https://opencode.ai/zen/v1`; shared key with OpenCode Go | -| OpenCode Go | `/provider` or env vars | $10/mo subscription for open models (20 models); uses `OPENCODE_API_KEY` via `https://opencode.ai/zen/go/v1`; shared key with OpenCode Zen | +| OpenCode Go | `/provider` or env vars | $10/mo subscription for open models (13 models); uses `OPENCODE_API_KEY` via `https://opencode.ai/zen/go/v1`; shared key with OpenCode Zen | | Xiaomi MiMo | `/provider` or env vars | OpenAI-compatible API at `https://mimo.mi.com`; uses `MIMO_API_KEY` and defaults to `mimo-v2.5-pro` | | NEAR AI | `/provider` or env vars | Unified gateway (Claude, GPT, Gemini + TEE open models); uses `NEARAI_API_KEY` at `https://cloud-api.near.ai/v1` | | Ollama | `/provider` or env vars | Local inference with no API key | diff --git a/docs/advanced-setup.md b/docs/advanced-setup.md index 664e55f324..9a1beb443c 100644 --- a/docs/advanced-setup.md +++ b/docs/advanced-setup.md @@ -212,7 +212,7 @@ export OPENAI_MODEL=glm-5.1 openclaude ``` -OpenCode Go is a $10/mo subscription for 20 open models (GLM, Kimi, DeepSeek, +OpenCode Go is a $10/mo subscription for 13 open models (GLM, Kimi, DeepSeek, MiMo, MiniMax, Qwen). Uses the same `OPENCODE_API_KEY` as OpenCode Zen. ### Gitlawb Opengateway diff --git a/src/integrations/gateways/opencode-go.ts b/src/integrations/gateways/opencode-go.ts index ffb3837114..6beeaf46c3 100644 --- a/src/integrations/gateways/opencode-go.ts +++ b/src/integrations/gateways/opencode-go.ts @@ -27,26 +27,19 @@ function catalogEntry(spec: OpenCodeGoCatalogSpec) { } const goModels: OpenCodeGoCatalogSpec[] = [ - { id: 'minimax-m3', apiName: 'minimax-m3', label: 'MiniMax M3', endpointPath: '/messages' }, - { id: 'minimax-m2.7', apiName: 'minimax-m2.7', label: 'MiniMax M2.7', endpointPath: '/messages' }, - { id: 'minimax-m2.5', apiName: 'minimax-m2.5', label: 'MiniMax M2.5', endpointPath: '/messages' }, - { id: 'kimi-k2.7-code', apiName: 'kimi-k2.7-code', label: 'Kimi K2.7 Code' }, - { id: 'kimi-k2.6', apiName: 'kimi-k2.6', label: 'Kimi K2.6' }, - { id: 'kimi-k2.5', apiName: 'kimi-k2.5', label: 'Kimi K2.5' }, { id: 'glm-5.2', apiName: 'glm-5.2', label: 'GLM 5.2' }, - { id: 'glm-5.1', apiName: 'glm-5.1', label: 'GLM 5.1' }, - { id: 'glm-5', apiName: 'glm-5', label: 'GLM 5' }, - { id: 'deepseek-v4-pro', apiName: 'deepseek-v4-pro', label: 'DeepSeek V4 Pro' }, - { id: 'deepseek-v4-flash', apiName: 'deepseek-v4-flash', label: 'DeepSeek V4 Flash' }, { id: 'qwen3.7-max', apiName: 'qwen3.7-max', label: 'Qwen3.7 Max', endpointPath: '/messages' }, - { id: 'qwen3.7-plus', apiName: 'qwen3.7-plus', label: 'Qwen3.7 Plus', endpointPath: '/messages' }, - { id: 'qwen3.6-plus', apiName: 'qwen3.6-plus', label: 'Qwen3.6 Plus', endpointPath: '/messages' }, - { id: 'qwen3.5-plus', apiName: 'qwen3.5-plus', label: 'Qwen3.5 Plus', endpointPath: '/messages' }, - { id: 'mimo-v2-pro', apiName: 'mimo-v2-pro', label: 'MiMo V2 Pro' }, - { id: 'mimo-v2-omni', apiName: 'mimo-v2-omni', label: 'MiMo V2 Omni' }, + { id: 'kimi-k2.7-code', apiName: 'kimi-k2.7-code', label: 'Kimi K2.7 Code' }, { id: 'mimo-v2.5-pro', apiName: 'mimo-v2.5-pro', label: 'MiMo V2.5 Pro' }, + { id: 'deepseek-v4-pro', apiName: 'deepseek-v4-pro', label: 'DeepSeek V4 Pro' }, + { id: 'qwen3.7-plus', apiName: 'qwen3.7-plus', label: 'Qwen3.7 Plus', endpointPath: '/messages' }, + { id: 'minimax-m3', apiName: 'minimax-m3', label: 'MiniMax M3', endpointPath: '/messages' }, { id: 'mimo-v2.5', apiName: 'mimo-v2.5', label: 'MiMo V2.5' }, - { id: 'hy3-preview', apiName: 'hy3-preview', label: 'HY3 Preview' }, + { id: 'deepseek-v4-flash', apiName: 'deepseek-v4-flash', label: 'DeepSeek V4 Flash' }, + { id: 'glm-5.1', apiName: 'glm-5.1', label: 'GLM 5.1' }, + { id: 'kimi-k2.6', apiName: 'kimi-k2.6', label: 'Kimi K2.6' }, + { id: 'qwen3.6-plus', apiName: 'qwen3.6-plus', label: 'Qwen3.6 Plus', endpointPath: '/messages' }, + { id: 'minimax-m2.7', apiName: 'minimax-m2.7', label: 'MiniMax M2.7', endpointPath: '/messages' }, ] export default defineGateway({ @@ -78,7 +71,7 @@ export default defineGateway({ preset: { id: 'opencode-go', vendorId: 'openai', - description: 'OpenCode Go - $10/mo subscription for open models (20 models)', + description: 'OpenCode Go - $10/mo subscription for open models (13 models)', apiKeyEnvVars: ['OPENCODE_API_KEY'], modelEnvVars: ['OPENAI_MODEL'], }, diff --git a/src/integrations/gateways/opencode.test.ts b/src/integrations/gateways/opencode.test.ts index 36a805d7c5..aecf6829a5 100644 --- a/src/integrations/gateways/opencode.test.ts +++ b/src/integrations/gateways/opencode.test.ts @@ -289,7 +289,48 @@ describe('OpenCode model catalog', () => { test('go model count matches expected', () => { const models = getCatalogEntriesForRoute('opencode-go') - expect(models.length).toBe(20) + expect(models.length).toBe(13) + }) + + test('go model set matches opencode.ai/go catalog exactly', () => { + const models = getCatalogEntriesForRoute('opencode-go') + const expectedIds = new Set([ + 'opencode-go-glm-5.2', + 'opencode-go-qwen3.7-max', + 'opencode-go-kimi-k2.7-code', + 'opencode-go-mimo-v2.5-pro', + 'opencode-go-deepseek-v4-pro', + 'opencode-go-qwen3.7-plus', + 'opencode-go-minimax-m3', + 'opencode-go-mimo-v2.5', + 'opencode-go-deepseek-v4-flash', + 'opencode-go-glm-5.1', + 'opencode-go-kimi-k2.6', + 'opencode-go-qwen3.6-plus', + 'opencode-go-minimax-m2.7', + ]) + const actualIds = new Set(models.map(m => m.id)) + // All expected IDs present + for (const id of expectedIds) { + expect(actualIds.has(id)).toBe(true) + } + // No removed or unexpected IDs remain + for (const id of actualIds) { + expect(expectedIds.has(id)).toBe(true) + } + // Explicitly verify removed models are not included + const removedIds = [ + 'opencode-go-minimax-m2.5', + 'opencode-go-kimi-k2.5', + 'opencode-go-glm-5', + 'opencode-go-qwen3.5-plus', + 'opencode-go-mimo-v2-pro', + 'opencode-go-mimo-v2-omni', + 'opencode-go-hy3-preview', + ]; + for (const id of removedIds) { + expect(actualIds.has(id)).toBe(false); + } }) test('all zen gpt models have modelDescriptorId', () => { @@ -323,11 +364,9 @@ describe('OpenCode model catalog', () => { const messagesModelIds = [ 'opencode-go-minimax-m3', 'opencode-go-minimax-m2.7', - 'opencode-go-minimax-m2.5', 'opencode-go-qwen3.7-max', 'opencode-go-qwen3.7-plus', 'opencode-go-qwen3.6-plus', - 'opencode-go-qwen3.5-plus', ] for (const id of messagesModelIds) { const model = models.find(m => m.id === id) @@ -477,8 +516,8 @@ describe('OpenCode edge cases', () => { expect(models.get('opencode-qwen3.6-plus')?.contextWindow).toBe(262_144) expect(models.get('opencode-deepseek-v4-pro')?.maxOutputTokens).toBe(384_000) - expect(models.get('opencode-go-minimax-m2.5')?.maxOutputTokens).toBe(65_536) - expect(models.get('opencode-go-hy3-preview')?.contextWindow).toBe(256_000) + expect(models.get('opencode-go-minimax-m2.7')?.maxOutputTokens).toBe(131_072) + expect(models.get('opencode-go-mimo-v2.5')?.contextWindow).toBe(1_000_000) }) test('zen gateway validation message mentions OPENCODE_API_KEY', () => { diff --git a/src/integrations/generated/integrationArtifacts.generated.ts b/src/integrations/generated/integrationArtifacts.generated.ts index 585ae5d3ac..885f837f85 100644 --- a/src/integrations/generated/integrationArtifacts.generated.ts +++ b/src/integrations/generated/integrationArtifacts.generated.ts @@ -357,7 +357,7 @@ export const PROVIDER_PRESET_MANIFEST = [ "routeId": "opencode-go", "vendorId": "openai", "gatewayId": "opencode-go", - "description": "OpenCode Go - $10/mo subscription for open models (20 models)", + "description": "OpenCode Go - $10/mo subscription for open models (13 models)", "apiKeyEnvVars": [ "OPENCODE_API_KEY" ], diff --git a/src/integrations/models/opencode.ts b/src/integrations/models/opencode.ts index 98781b1e73..5edb1a7c6a 100644 --- a/src/integrations/models/opencode.ts +++ b/src/integrations/models/opencode.ts @@ -96,26 +96,19 @@ const zenModels: OpenCodeModelSpec[] = [ ] const goModels: OpenCodeModelSpec[] = [ - { id: 'opencode-go-minimax-m3', label: 'MiniMax M3', defaultModel: 'minimax-m3', contextWindow: 512_000, maxOutputTokens: 131_072, reasoning: true, coding: true }, - { id: 'opencode-go-minimax-m2.7', label: 'MiniMax M2.7', defaultModel: 'minimax-m2.7', contextWindow: 204_800, maxOutputTokens: 131_072, reasoning: true, vision: true, coding: true }, - { id: 'opencode-go-minimax-m2.5', label: 'MiniMax M2.5', defaultModel: 'minimax-m2.5', contextWindow: 204_800, maxOutputTokens: 65_536, reasoning: true, vision: true, coding: true }, - { id: 'opencode-go-kimi-k2.7-code', label: 'Kimi K2.7 Code', defaultModel: 'kimi-k2.7-code', contextWindow: 262_144, maxOutputTokens: 262_144, reasoning: true, coding: true }, - { id: 'opencode-go-kimi-k2.6', label: 'Kimi K2.6', defaultModel: 'kimi-k2.6', contextWindow: 262_144, maxOutputTokens: 65_536, reasoning: true, coding: true }, - { id: 'opencode-go-kimi-k2.5', label: 'Kimi K2.5', defaultModel: 'kimi-k2.5', contextWindow: 262_144, maxOutputTokens: 65_536, reasoning: true, coding: true }, { id: 'opencode-go-glm-5.2', label: 'GLM 5.2', defaultModel: 'glm-5.2', contextWindow: 1_000_000, maxOutputTokens: 131_072, coding: true }, - { id: 'opencode-go-glm-5.1', label: 'GLM 5.1', defaultModel: 'glm-5.1', contextWindow: 202_752, maxOutputTokens: 32_768, coding: true }, - { id: 'opencode-go-glm-5', label: 'GLM 5', defaultModel: 'glm-5', contextWindow: 202_752, maxOutputTokens: 32_768, coding: true }, - { id: 'opencode-go-deepseek-v4-pro', label: 'DeepSeek V4 Pro', defaultModel: 'deepseek-v4-pro', contextWindow: 1_000_000, maxOutputTokens: 384_000, reasoning: true, coding: true }, - { id: 'opencode-go-deepseek-v4-flash', label: 'DeepSeek V4 Flash', defaultModel: 'deepseek-v4-flash', contextWindow: 1_000_000, maxOutputTokens: 384_000, coding: true }, { id: 'opencode-go-qwen3.7-max', label: 'Qwen3.7 Max', defaultModel: 'qwen3.7-max', contextWindow: 1_000_000, maxOutputTokens: 65_536, reasoning: true, coding: true }, - { id: 'opencode-go-qwen3.7-plus', label: 'Qwen3.7 Plus', defaultModel: 'qwen3.7-plus', contextWindow: 1_000_000, maxOutputTokens: 65_536, reasoning: true, coding: true }, - { id: 'opencode-go-qwen3.6-plus', label: 'Qwen3.6 Plus', defaultModel: 'qwen3.6-plus', contextWindow: 1_000_000, maxOutputTokens: 65_536, reasoning: true, coding: true }, - { id: 'opencode-go-qwen3.5-plus', label: 'Qwen3.5 Plus', defaultModel: 'qwen3.5-plus', contextWindow: 262_144, maxOutputTokens: 65_536, reasoning: true, coding: true }, - { id: 'opencode-go-mimo-v2-pro', label: 'MiMo V2 Pro', defaultModel: 'mimo-v2-pro', contextWindow: 1_048_576, maxOutputTokens: 128_000, reasoning: true, coding: true }, - { id: 'opencode-go-mimo-v2-omni', label: 'MiMo V2 Omni', defaultModel: 'mimo-v2-omni', contextWindow: 262_144, maxOutputTokens: 128_000, reasoning: true, vision: true, coding: true }, + { id: 'opencode-go-kimi-k2.7-code', label: 'Kimi K2.7 Code', defaultModel: 'kimi-k2.7-code', contextWindow: 262_144, maxOutputTokens: 262_144, reasoning: true, coding: true }, { id: 'opencode-go-mimo-v2.5-pro', label: 'MiMo V2.5 Pro', defaultModel: 'mimo-v2.5-pro', contextWindow: 1_048_576, maxOutputTokens: 128_000, reasoning: true, coding: true }, + { id: 'opencode-go-deepseek-v4-pro', label: 'DeepSeek V4 Pro', defaultModel: 'deepseek-v4-pro', contextWindow: 1_000_000, maxOutputTokens: 384_000, reasoning: true, coding: true }, + { id: 'opencode-go-qwen3.7-plus', label: 'Qwen3.7 Plus', defaultModel: 'qwen3.7-plus', contextWindow: 1_000_000, maxOutputTokens: 65_536, reasoning: true, coding: true }, + { id: 'opencode-go-minimax-m3', label: 'MiniMax M3', defaultModel: 'minimax-m3', contextWindow: 512_000, maxOutputTokens: 131_072, reasoning: true, coding: true }, { id: 'opencode-go-mimo-v2.5', label: 'MiMo V2.5', defaultModel: 'mimo-v2.5', contextWindow: 1_000_000, maxOutputTokens: 128_000, reasoning: true, coding: true }, - { id: 'opencode-go-hy3-preview', label: 'HY3 Preview', defaultModel: 'hy3-preview', contextWindow: 256_000, maxOutputTokens: 64_000, coding: true }, + { id: 'opencode-go-deepseek-v4-flash', label: 'DeepSeek V4 Flash', defaultModel: 'deepseek-v4-flash', contextWindow: 1_000_000, maxOutputTokens: 384_000, coding: true }, + { id: 'opencode-go-glm-5.1', label: 'GLM 5.1', defaultModel: 'glm-5.1', contextWindow: 202_752, maxOutputTokens: 32_768, coding: true }, + { id: 'opencode-go-kimi-k2.6', label: 'Kimi K2.6', defaultModel: 'kimi-k2.6', contextWindow: 262_144, maxOutputTokens: 65_536, reasoning: true, coding: true }, + { id: 'opencode-go-qwen3.6-plus', label: 'Qwen3.6 Plus', defaultModel: 'qwen3.6-plus', contextWindow: 1_000_000, maxOutputTokens: 65_536, reasoning: true, coding: true }, + { id: 'opencode-go-minimax-m2.7', label: 'MiniMax M2.7', defaultModel: 'minimax-m2.7', contextWindow: 204_800, maxOutputTokens: 131_072, reasoning: true, vision: true, coding: true }, ] export default [...zenModels, ...goModels].map(openCodeModel) diff --git a/src/services/api/openaiShim.test.ts b/src/services/api/openaiShim.test.ts index b65667cbb1..d62b4785bd 100644 --- a/src/services/api/openaiShim.test.ts +++ b/src/services/api/openaiShim.test.ts @@ -2452,9 +2452,9 @@ test('xiaomi mimo route uses api-key auth header and max_completion_tokens', asy test.each([ 'minimax-m3', 'minimax-m2.7', - 'minimax-m2.5', + 'qwen3.7-max', + 'qwen3.7-plus', 'qwen3.6-plus', - 'qwen3.5-plus', ])('opencode go %s direct env routing ignores stale custom auth and uses the Anthropic Messages request contract', async model => { let capturedUrl = '' let capturedHeaders: Headers | undefined