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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down
2 changes: 1 addition & 1 deletion docs/advanced-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
27 changes: 10 additions & 17 deletions src/integrations/gateways/opencode-go.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down Expand Up @@ -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'],
},
Expand Down
49 changes: 44 additions & 5 deletions src/integrations/gateways/opencode.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
})
Comment thread
coderabbitai[bot] marked this conversation as resolved.

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', () => {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
],
Expand Down
25 changes: 9 additions & 16 deletions src/integrations/models/opencode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
4 changes: 2 additions & 2 deletions src/services/api/openaiShim.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down