Skip to content

Commit 497cc4a

Browse files
committed
Route opencode chat models through Zen
1 parent cba6d1d commit 497cc4a

2 files changed

Lines changed: 48 additions & 29 deletions

File tree

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

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -869,13 +869,24 @@ describe('/api/v1/chat/completions POST endpoint', () => {
869869
)
870870

871871
it(
872-
'routes OpenCode Zen models to the direct OpenCode Zen provider',
872+
'routes OpenCode Zen-prefixed and Kimi models to the direct OpenCode Zen provider',
873873
async () => {
874-
const expectedUpstreamModel: Record<string, string> = {
875-
'opencode/kimi-k2.6': 'kimi-k2.6',
876-
}
874+
const testCases = [
875+
{
876+
codebuffModel: openCodeZenModels.opencode_kimi_k2_6,
877+
upstreamModel: 'kimi-k2.6',
878+
},
879+
{
880+
codebuffModel: 'opencode/qwen3-coder',
881+
upstreamModel: 'qwen3-coder',
882+
},
883+
{
884+
codebuffModel: 'moonshotai/kimi-k2.6',
885+
upstreamModel: 'kimi-k2.6',
886+
},
887+
]
877888

878-
for (const codebuffModel of Object.values(openCodeZenModels)) {
889+
for (const { codebuffModel, upstreamModel } of testCases) {
879890
const fetchedBodies: Record<string, unknown>[] = []
880891
const fetchedUrls: string[] = []
881892
const fetchViaOpenCodeZen = mock(
@@ -889,7 +900,7 @@ describe('/api/v1/chat/completions POST endpoint', () => {
889900
return new Response(
890901
JSON.stringify({
891902
id: 'test-id',
892-
model: expectedUpstreamModel[codebuffModel],
903+
model: upstreamModel,
893904
choices: [{ message: { content: 'test response' } }],
894905
usage: {
895906
prompt_tokens: 10,
@@ -968,9 +979,7 @@ describe('/api/v1/chat/completions POST endpoint', () => {
968979
expect(fetchedUrls[0]).toBe(
969980
'https://opencode.ai/zen/v1/chat/completions',
970981
)
971-
expect(fetchedBodies[0].model).toBe(
972-
expectedUpstreamModel[codebuffModel],
973-
)
982+
expect(fetchedBodies[0].model).toBe(upstreamModel)
974983
expect(body.model).toBe(codebuffModel)
975984
expect(body.provider).toBe('OpenCode Zen')
976985
}

web/src/llm-api/opencode-zen.ts

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -34,34 +34,44 @@ interface OpenCodeZenPricing {
3434
outputCostPerToken: number
3535
}
3636

37-
const OPENCODE_ZEN_MODELS: Record<
38-
string,
39-
{ opencodeId: string; pricing: OpenCodeZenPricing }
40-
> = {
41-
[openCodeZenModels.opencode_kimi_k2_6]: {
42-
opencodeId: 'kimi-k2.6',
43-
pricing: {
44-
inputCostPerToken: 0.95 / 1_000_000,
45-
cachedInputCostPerToken: 0.16 / 1_000_000,
46-
outputCostPerToken: 4.0 / 1_000_000,
47-
},
48-
},
37+
const OPENCODE_MODEL_PREFIX = 'opencode/'
38+
const MOONSHOT_KIMI_MODEL = 'moonshotai/kimi-k2.6'
39+
const KIMI_ZEN_MODEL = 'kimi-k2.6'
40+
41+
const OPENCODE_ZEN_MODEL_ALIASES: Record<string, string> = {
42+
[openCodeZenModels.opencode_kimi_k2_6]: KIMI_ZEN_MODEL,
43+
[MOONSHOT_KIMI_MODEL]: KIMI_ZEN_MODEL,
4944
}
5045

51-
export function isOpenCodeZenModel(model: string): boolean {
52-
return model in OPENCODE_ZEN_MODELS
46+
const KIMI_ZEN_PRICING: OpenCodeZenPricing = {
47+
inputCostPerToken: 0.95 / 1_000_000,
48+
cachedInputCostPerToken: 0.16 / 1_000_000,
49+
outputCostPerToken: 4.0 / 1_000_000,
50+
}
51+
52+
const OPENCODE_ZEN_PRICING: Record<string, OpenCodeZenPricing> = {
53+
[KIMI_ZEN_MODEL]: KIMI_ZEN_PRICING,
54+
}
55+
56+
export function isOpenCodeZenModel(model: unknown): model is string {
57+
if (typeof model !== 'string') return false
58+
return (
59+
model.startsWith(OPENCODE_MODEL_PREFIX) ||
60+
model in OPENCODE_ZEN_MODEL_ALIASES
61+
)
5362
}
5463

5564
function getOpenCodeZenModelId(model: string): string {
56-
return OPENCODE_ZEN_MODELS[model]?.opencodeId ?? model
65+
return (
66+
OPENCODE_ZEN_MODEL_ALIASES[model] ??
67+
(model.startsWith(OPENCODE_MODEL_PREFIX)
68+
? model.slice(OPENCODE_MODEL_PREFIX.length)
69+
: model)
70+
)
5771
}
5872

5973
function getOpenCodeZenPricing(model: string): OpenCodeZenPricing {
60-
const entry = OPENCODE_ZEN_MODELS[model]
61-
if (!entry) {
62-
throw new Error(`No OpenCode Zen pricing found for model: ${model}`)
63-
}
64-
return entry.pricing
74+
return OPENCODE_ZEN_PRICING[getOpenCodeZenModelId(model)] ?? KIMI_ZEN_PRICING
6575
}
6676

6777
type StreamState = {

0 commit comments

Comments
 (0)