Skip to content
Open
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
14 changes: 14 additions & 0 deletions agents/base2/base2-free-opencode-kimi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { openCodeZenModels } from '@codebuff/common/constants/model-config'

import { createBase2 } from './base2'

const definition = {
...createBase2('free', {
noAskUser: true,
model: openCodeZenModels.opencode_kimi_k2_6,
}),
id: 'base2-free-opencode-kimi',
displayName: 'Buffy the Kimi (OpenCode) Free Orchestrator',
}

export default definition
14 changes: 14 additions & 0 deletions agents/base2/base2-free-opencode-minimax.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { openCodeZenModels } from '@codebuff/common/constants/model-config'

import { createBase2 } from './base2'

const definition = {
...createBase2('free', {
noAskUser: true,
model: openCodeZenModels.opencode_minimax_m2_7,
}),
id: 'base2-free-opencode-minimax',
displayName: 'Buffy the MiniMax (OpenCode) Free Orchestrator',
}

export default definition
2 changes: 1 addition & 1 deletion evals/buffbench/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ async function main() {
// Use 'external:opencode' for OpenCode CLI
await runBuffBench({
evalDataPaths: [path.join(__dirname, 'eval-codebuff.json')],
agents: ['base2-free-evals'],
agents: ['base2-free-kimi'],
taskConcurrency: 6,
saveTraces,
})
Expand Down
54 changes: 42 additions & 12 deletions web/src/app/api/v1/chat/completions/__tests__/completions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -854,15 +854,44 @@ describe('/api/v1/chat/completions POST endpoint', () => {
)

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

for (const codebuffModel of Object.values(openCodeZenModels)) {
const fetchedBodies: Record<string, unknown>[] = []
const fetchedUrls: string[] = []
const fetchViaOpenCodeZen = mock(
async (url: string | URL | Request, init?: RequestInit) => {
if (String(url).startsWith('https://api.ipinfo.io/lookup/')) {
return Response.json({})
}

fetchedUrls.push(String(url))
fetchedBodies.push(JSON.parse(init?.body as string))
return new Response(
JSON.stringify({
id: 'test-id',
model: expectedUpstreamModel[codebuffModel],
choices: [{ message: { content: 'test response' } }],
usage: {
prompt_tokens: 10,
prompt_tokens_details: { cached_tokens: 4 },
completion_tokens: 20,
total_tokens: 30,
},
}),
{
status: 200,
headers: { 'Content-Type': 'application/json' },
},
)
},
) as unknown as typeof globalThis.fetch

const req = new NextRequest(
'http://localhost:3000/api/v1/chat/completions',
{
Expand Down Expand Up @@ -921,13 +950,14 @@ describe('/api/v1/chat/completions POST endpoint', () => {
})

const body = await response.json()
expect(response.status).toBe(400)
expect(body).toEqual({
error: 'opencode_zen_disabled',
message: 'OpenCode Zen models are currently disabled.',
})
expect(response.status).toBe(200)
expect(fetchedUrls[0]).toBe(
'https://opencode.ai/zen/v1/chat/completions',
)
expect(fetchedBodies[0].model).toBe(expectedUpstreamModel[codebuffModel])
expect(body.model).toBe(codebuffModel)
expect(body.provider).toBe('OpenCode Zen')
}
expect(fetchViaOpenCodeZen).not.toHaveBeenCalled()
},
FETCH_PATH_TEST_TIMEOUT_MS,
)
Expand Down
Loading