Skip to content

Commit ebf63c5

Browse files
myk1ytedelauna
authored andcommitted
fix(opencode-go): add MiMo V2.6 Flash and Pro model configurations (#2246)
What: add mimo-v2.6-flash and mimo-v2.6-pro to the curated OpenCode Go registry in packages/types, with their own context/output limits, input modalities, reasoning preservation, and Go pricing. Why: both IDs are served by the live Go catalog but were uncurated, so the fetcher fell back to 200,000 context / 32,768 output, no image support, no pricing, and no reasoning preservation. Issue #1997 asks for independently confirmed V2.6 metadata rather than a copy of the V2.5 limits. Impact: the two models now report 1,048,576 context / 131,072 output, accept image input, preserve reasoning across tool-call continuations, and price at 0.14/0.28/0.0028 (Flash) and 0.435/0.87/0.003625 (Pro) per 1M tokens. Routing is unchanged: both stay on Chat Completions. No other model is affected. Fixes #1997
1 parent 43d8d85 commit ebf63c5

4 files changed

Lines changed: 179 additions & 0 deletions

File tree

‎packages/types/src/__tests__/opencode-go.test.ts‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ describe("opencode-go registry", () => {
3434
"kimi-k2.6",
3535
"mimo-v2.5",
3636
"mimo-v2.5-pro",
37+
"mimo-v2.6-flash",
38+
"mimo-v2.6-pro",
3739
"deepseek-v4-pro",
3840
"deepseek-v4-flash",
3941
"deepseek-v4.1-flash",
@@ -361,6 +363,32 @@ describe("opencode-go registry", () => {
361363
cacheReadsPrice: 0.08,
362364
},
363365
},
366+
{
367+
id: "mimo-v2.6-flash",
368+
expected: {
369+
maxTokens: 131_072,
370+
contextWindow: 1_048_576,
371+
supportsImages: true,
372+
supportsPromptCache: false,
373+
preserveReasoning: true,
374+
inputPrice: 0.14,
375+
outputPrice: 0.28,
376+
cacheReadsPrice: 0.0028,
377+
},
378+
},
379+
{
380+
id: "mimo-v2.6-pro",
381+
expected: {
382+
maxTokens: 131_072,
383+
contextWindow: 1_048_576,
384+
supportsImages: true,
385+
supportsPromptCache: false,
386+
preserveReasoning: true,
387+
inputPrice: 0.435,
388+
outputPrice: 0.87,
389+
cacheReadsPrice: 0.003625,
390+
},
391+
},
364392
{
365393
id: "qwen3.5-plus",
366394
expected: {

‎packages/types/src/providers/opencode-go.ts‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,30 @@ export const opencodeGoModels: Record<string, ModelInfo> = {
276276
description:
277277
"MiMo V2 Omni is Xiaomi's multimodal reasoning model with a 256k context window. Available via the Opencode Go plan.",
278278
},
279+
"mimo-v2.6-flash": {
280+
maxTokens: 131_072,
281+
contextWindow: 1_048_576,
282+
supportsImages: true,
283+
supportsPromptCache: false,
284+
preserveReasoning: true,
285+
inputPrice: 0.14,
286+
outputPrice: 0.28,
287+
cacheReadsPrice: 0.0028,
288+
description:
289+
"MiMo V2.6 Flash is Xiaomi's fast full-modal model (text, image, audio, video) with 1M context and deep thinking. Available via the Opencode Go plan.",
290+
},
291+
"mimo-v2.6-pro": {
292+
maxTokens: 131_072,
293+
contextWindow: 1_048_576,
294+
supportsImages: true,
295+
supportsPromptCache: false,
296+
preserveReasoning: true,
297+
inputPrice: 0.435,
298+
outputPrice: 0.87,
299+
cacheReadsPrice: 0.003625,
300+
description:
301+
"MiMo V2.6 Pro is Xiaomi's stronger full-modal reasoning model with 1M context and deep thinking. Available via the Opencode Go plan.",
302+
},
279303

280304
// --- MiniMax ---
281305
"minimax-m2.5": {

‎src/api/providers/__tests__/opencode-go.spec.ts‎

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,78 @@ describe("OpencodeGoHandler", () => {
378378
expect(callArgs.messages.filter((m) => m.role === "user")).toHaveLength(1)
379379
})
380380

381+
it("preserves reasoning across a tool-call continuation for MiMo V2.6 Flash", async () => {
382+
// Xiaomi's deep-thinking guide requires the reasoning from the assistant
383+
// turn to be passed back on the tool-call continuation. MiMo V2.6 must
384+
// therefore convert through convertToR1Format (preserveReasoning) and
385+
// keep the reasoning -> tool_call -> tool_result history intact.
386+
vitest.mocked(getModels).mockImplementationOnce(async () => ({
387+
"mimo-v2.6-flash": { ...opencodeGoModels["mimo-v2.6-flash"] },
388+
}))
389+
mockCreate.mockImplementationOnce(async () =>
390+
asyncStreamFrom([
391+
{ choices: [{ delta: { content: "Done" }, index: 0 }] },
392+
{
393+
choices: [{ delta: {}, index: 0 }],
394+
usage: { prompt_tokens: 1, completion_tokens: 1, total_tokens: 2 },
395+
},
396+
]),
397+
)
398+
399+
const handler = new OpencodeGoHandler({ ...mockOptions, opencodeGoModelId: "mimo-v2.6-flash" })
400+
const messages: Anthropic.Messages.MessageParam[] = [
401+
{ role: "user", content: "Read the config file" },
402+
{
403+
role: "assistant",
404+
content: [
405+
{ type: "text", text: "I will read it." },
406+
{ type: "tool_use", id: "call_mimo_1", name: "read_file", input: { path: "config.json" } },
407+
],
408+
reasoning_content: "The user wants the config contents.",
409+
} as Anthropic.Messages.MessageParam,
410+
{
411+
role: "user",
412+
content: [
413+
{ type: "tool_result", tool_use_id: "call_mimo_1", content: '{"a":1}' },
414+
{ type: "text", text: "<environment_details>cwd: /repo</environment_details>" },
415+
],
416+
},
417+
]
418+
419+
await collectStream(handler.createMessage("sys", messages))
420+
421+
const callArgs = mockCreate.mock.calls[0][0] as {
422+
model: string
423+
messages: Array<Record<string, unknown>>
424+
}
425+
expect(callArgs.model).toBe("mimo-v2.6-flash")
426+
427+
// Index 0 is the system prompt; the converted history follows it.
428+
expect(callArgs.messages[0]).toEqual({ role: "system", content: "sys" })
429+
const [, userMessage, assistantMessage, toolMessage] = callArgs.messages
430+
expect(userMessage).toEqual({ role: "user", content: "Read the config file" })
431+
// The assistant turn keeps both its reasoning and its tool call.
432+
expect(assistantMessage).toMatchObject({
433+
role: "assistant",
434+
reasoning_content: "The user wants the config contents.",
435+
})
436+
expect(assistantMessage.tool_calls).toEqual([
437+
{
438+
id: "call_mimo_1",
439+
type: "function",
440+
function: { name: "read_file", arguments: '{"path":"config.json"}' },
441+
},
442+
])
443+
// The tool result follows as a tool message, with the trailing
444+
// environment details merged in so no user turn drops the reasoning.
445+
expect(toolMessage).toEqual({
446+
role: "tool",
447+
tool_call_id: "call_mimo_1",
448+
content: '{"a":1}\n\n<environment_details>cwd: /repo</environment_details>',
449+
})
450+
expect(callArgs.messages.filter((message) => message.role === "user")).toHaveLength(1)
451+
})
452+
381453
it("emits a usage chunk with zeroed tokens when the stream reports no usage", async () => {
382454
mockCreate.mockImplementationOnce(async () =>
383455
asyncStreamFrom([

‎src/api/providers/fetchers/__tests__/opencode-go.spec.ts‎

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,37 @@ describe("Opencode Go Fetchers", () => {
163163
})
164164
})
165165

166+
it("resolves MiMo V2.6 Flash and Pro from an ID-only catalog response", async () => {
167+
mockedAxios.get.mockResolvedValue({
168+
data: { data: [{ id: "mimo-v2.6-flash" }, { id: "mimo-v2.6-pro" }] },
169+
})
170+
171+
const models = await getOpencodeGoModels("k")
172+
173+
// The generic fallback would report 200_000 context / 32_768 output and
174+
// no pricing, so these values prove the native registry was used.
175+
expect(models["mimo-v2.6-flash"]).toMatchObject({
176+
contextWindow: 1_048_576,
177+
maxTokens: 131_072,
178+
supportsImages: true,
179+
supportsPromptCache: false,
180+
preserveReasoning: true,
181+
inputPrice: 0.14,
182+
outputPrice: 0.28,
183+
cacheReadsPrice: 0.0028,
184+
})
185+
expect(models["mimo-v2.6-pro"]).toMatchObject({
186+
contextWindow: 1_048_576,
187+
maxTokens: 131_072,
188+
supportsImages: true,
189+
supportsPromptCache: false,
190+
preserveReasoning: true,
191+
inputPrice: 0.435,
192+
outputPrice: 0.87,
193+
cacheReadsPrice: 0.003625,
194+
})
195+
})
196+
166197
it("returns an empty map on network error", async () => {
167198
mockedAxios.get.mockRejectedValue(new Error("network"))
168199
expect(await getOpencodeGoModels("k")).toEqual({})
@@ -253,6 +284,8 @@ describe("Opencode Go Fetchers", () => {
253284
"mimo-v2-omni",
254285
"mimo-v2.5-pro",
255286
"mimo-v2.5",
287+
"mimo-v2.6-flash",
288+
"mimo-v2.6-pro",
256289
"hy4-preview",
257290
"hy3",
258291
"hy3-preview",
@@ -330,6 +363,28 @@ describe("Opencode Go Fetchers", () => {
330363
expect(info.cacheReadsPrice).toBe(0.006)
331364
})
332365

366+
it("resolves MiMo V2.6 Flash and Pro to their 1M context when live metadata is absent", () => {
367+
const flash = parseOpencodeGoModel({ id: "mimo-v2.6-flash" })
368+
expect(flash.contextWindow).toBe(1_048_576)
369+
expect(flash.maxTokens).toBe(131_072)
370+
expect(flash.supportsImages).toBe(true)
371+
expect(flash.supportsPromptCache).toBe(false)
372+
expect(flash.preserveReasoning).toBe(true)
373+
expect(flash.inputPrice).toBe(0.14)
374+
expect(flash.outputPrice).toBe(0.28)
375+
expect(flash.cacheReadsPrice).toBe(0.0028)
376+
377+
const pro = parseOpencodeGoModel({ id: "mimo-v2.6-pro" })
378+
expect(pro.contextWindow).toBe(1_048_576)
379+
expect(pro.maxTokens).toBe(131_072)
380+
expect(pro.supportsImages).toBe(true)
381+
expect(pro.supportsPromptCache).toBe(false)
382+
expect(pro.preserveReasoning).toBe(true)
383+
expect(pro.inputPrice).toBe(0.435)
384+
expect(pro.outputPrice).toBe(0.87)
385+
expect(pro.cacheReadsPrice).toBe(0.003625)
386+
})
387+
333388
it("resolves GLM-5.2 with its 1M context and High/Max reasoning effort", () => {
334389
const info = parseOpencodeGoModel({ id: "glm-5.2" })
335390
expect(info.contextWindow).toBe(1_000_000)

0 commit comments

Comments
 (0)