Skip to content

Commit 3deb167

Browse files
jahoomaclaude
andcommitted
Enable OpenCode Zen for any opencode/-prefixed model
Routes any model id with the 'opencode/' prefix through the OpenCode Zen direct provider (strips the prefix before forwarding upstream), replacing the disabled-state rejection. Priced models still resolve through OPENCODE_ZEN_MODELS for billing; routing no longer depends on the priced lookup. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 63caaac commit 3deb167

3 files changed

Lines changed: 149 additions & 170 deletions

File tree

web/src/app/api/v1/chat/completions/__tests__/completions.test.ts

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -854,15 +854,44 @@ describe('/api/v1/chat/completions POST endpoint', () => {
854854
)
855855

856856
it(
857-
'rejects OpenCode Zen models while the Zen integration is disabled',
857+
'routes opencode/-prefixed models to the OpenCode Zen provider',
858858
async () => {
859-
const fetchViaOpenCodeZen = mock(
860-
async (_url: string | URL | Request, _init?: RequestInit) => {
861-
throw new Error('OpenCode Zen should not be called')
862-
},
863-
) as unknown as typeof globalThis.fetch
859+
const expectedUpstreamModel: Record<string, string> = {
860+
'opencode/minimax-m2.7': 'minimax-m2.7',
861+
'opencode/kimi-k2.6': 'kimi-k2.6',
862+
}
864863

865864
for (const codebuffModel of Object.values(openCodeZenModels)) {
865+
const fetchedBodies: Record<string, unknown>[] = []
866+
const fetchedUrls: string[] = []
867+
const fetchViaOpenCodeZen = mock(
868+
async (url: string | URL | Request, init?: RequestInit) => {
869+
if (String(url).startsWith('https://api.ipinfo.io/lookup/')) {
870+
return Response.json({})
871+
}
872+
873+
fetchedUrls.push(String(url))
874+
fetchedBodies.push(JSON.parse(init?.body as string))
875+
return new Response(
876+
JSON.stringify({
877+
id: 'test-id',
878+
model: expectedUpstreamModel[codebuffModel],
879+
choices: [{ message: { content: 'test response' } }],
880+
usage: {
881+
prompt_tokens: 10,
882+
prompt_tokens_details: { cached_tokens: 4 },
883+
completion_tokens: 20,
884+
total_tokens: 30,
885+
},
886+
}),
887+
{
888+
status: 200,
889+
headers: { 'Content-Type': 'application/json' },
890+
},
891+
)
892+
},
893+
) as unknown as typeof globalThis.fetch
894+
866895
const req = new NextRequest(
867896
'http://localhost:3000/api/v1/chat/completions',
868897
{
@@ -921,13 +950,16 @@ describe('/api/v1/chat/completions POST endpoint', () => {
921950
})
922951

923952
const body = await response.json()
924-
expect(response.status).toBe(400)
925-
expect(body).toEqual({
926-
error: 'opencode_zen_disabled',
927-
message: 'OpenCode Zen models are currently disabled.',
928-
})
953+
expect(response.status).toBe(200)
954+
expect(fetchedUrls[0]).toBe(
955+
'https://opencode.ai/zen/v1/chat/completions',
956+
)
957+
expect(fetchedBodies[0].model).toBe(
958+
expectedUpstreamModel[codebuffModel],
959+
)
960+
expect(body.model).toBe(codebuffModel)
961+
expect(body.provider).toBe('OpenCode Zen')
929962
}
930-
expect(fetchViaOpenCodeZen).not.toHaveBeenCalled()
931963
},
932964
FETCH_PATH_TEST_TIMEOUT_MS,
933965
)

0 commit comments

Comments
 (0)