Skip to content

Commit c2f77db

Browse files
feat: Add Xiaomi MiMo as a first-class API provider (Zoo-Code-Org#81)
* FEATURE: Add Xiaomi MiMo as a first-class API provider - New MimoHandler with reasoning_content passthrough for multi-turn tool calling - Custom message conversion preserving MiMo's interleaved thinking chain - Strip OpenAI-specific extensions (strict, additionalProperties) from tool schemas - Models: mimo-v2.5-pro, mimo-v2.5, mimo-v2-flash with official pricing - UI settings with 4 base URL options (3 Token Plan regions + Pay-as-you-go) - Full wiring: types, schema, model picker, i18n, provider config * Update openrouter.ts * Preserve Mimo reasoning fields & switch AMS endpoint Update Mimo provider config and UI to use the token-plan-ams endpoint instead of the old FRA host. Allow Mimo assistant messages with string content to include and preserve a reasoning_content field when present. Also avoid attaching mapped reasoning_details when converting messages for Mimo models (skip mapping if modelId matches /mimo/i) to preserve the provider's original shape. Types, API handler, transform logic, and the settings UI were updated to keep Mimo-specific reasoning data intact and align the endpoint selection. * Add MiMo i18n translations to all 17 locales and unit tests Also hide the "not sure which model" hint for the MiMo provider since it's a static model list, not fetched dynamically. * Address CodeRabbit review feedback Fix Japanese translation consistency, strengthen base URL tests to verify actual values, and add ModelPicker test for MiMo hint hiding. * Expand MiMo test coverage and remove dev/null files from PR Added tests for tool_call_partial streaming, cache token usage, API error handling, message conversion pipeline, empty delta chunks, and tools parameter presence/absence. Removed dev/null/ hook files that were accidentally included in the branch. * Use data-testid for automaticFetch hint instead of i18n key matching * Remove mimo-v2-flash model that no longer supports thinking mode * Replace mimo-v2-flash with mimo-v2-pro model * Drop mimo-v2-flash since it doesn't support thinking mode * Address maintainer feedback: revert unrelated changes, add i18n, fix stream_options * Update doc links to use puter developer URLs * Fix i18n: JP spacing, FR translations, add multimodal support to message conversion * Add docstrings and document mimo-v2-flash exclusion rationale * Address edelauna review: sanitize tool IDs, use handleProviderError, fix prompt cache, clean up comments * Fold text into last tool message to preserve reasoning continuity * Refactor: use shared convertToR1Format, processToolCalls, cleanup unused imports/props * Remove dead temperature param, add 2-tier pricing, clarify strict stripping * Remove strict/additionalProperties stripping — proxy no longer rejects it * Use longContextPricing instead of tiers for cost calculation * Remove stale convertToolsForOpenAI tests (stripping removed)
1 parent 7245680 commit c2f77db

35 files changed

Lines changed: 1216 additions & 6 deletions

packages/types/src/provider-settings.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
xaiModels,
2121
internationalZAiModels,
2222
minimaxModels,
23+
mimoModels,
2324
} from "./providers/index.js"
2425

2526
/**
@@ -121,6 +122,7 @@ export const providerNames = [
121122
"mistral",
122123
"moonshot",
123124
"minimax",
125+
"mimo",
124126
"openai-codex",
125127
"openai-native",
126128
"qwen-code",
@@ -334,6 +336,18 @@ const minimaxSchema = apiModelIdProviderModelSchema.extend({
334336
minimaxApiKey: z.string().optional(),
335337
})
336338

339+
const mimoSchema = apiModelIdProviderModelSchema.extend({
340+
mimoBaseUrl: z
341+
.union([
342+
z.literal("https://api.xiaomimimo.com/v1"),
343+
z.literal("https://token-plan-cn.xiaomimimo.com/v1"),
344+
z.literal("https://token-plan-sgp.xiaomimimo.com/v1"),
345+
z.literal("https://token-plan-ams.xiaomimimo.com/v1"),
346+
])
347+
.optional(),
348+
mimoApiKey: z.string().optional(),
349+
})
350+
337351
const requestySchema = baseProviderSettingsSchema.extend({
338352
requestyBaseUrl: z.string().optional(),
339353
requestyApiKey: z.string().optional(),
@@ -417,6 +431,7 @@ export const providerSettingsSchemaDiscriminated = z.discriminatedUnion("apiProv
417431
poeSchema.merge(z.object({ apiProvider: z.literal("poe") })),
418432
moonshotSchema.merge(z.object({ apiProvider: z.literal("moonshot") })),
419433
minimaxSchema.merge(z.object({ apiProvider: z.literal("minimax") })),
434+
mimoSchema.merge(z.object({ apiProvider: z.literal("mimo") })),
420435
requestySchema.merge(z.object({ apiProvider: z.literal("requesty") })),
421436
unboundSchema.merge(z.object({ apiProvider: z.literal("unbound") })),
422437
fakeAiSchema.merge(z.object({ apiProvider: z.literal("fake-ai") })),
@@ -451,6 +466,7 @@ export const providerSettingsSchema = z.object({
451466
...poeSchema.shape,
452467
...moonshotSchema.shape,
453468
...minimaxSchema.shape,
469+
...mimoSchema.shape,
454470
...requestySchema.shape,
455471
...unboundSchema.shape,
456472
...fakeAiSchema.shape,
@@ -525,6 +541,7 @@ export const modelIdKeysByProvider: Record<TypicalProvider, ModelIdKey> = {
525541
mistral: "apiModelId",
526542
moonshot: "apiModelId",
527543
minimax: "apiModelId",
544+
mimo: "apiModelId",
528545
deepseek: "apiModelId",
529546
poe: "apiModelId",
530547
"qwen-code": "apiModelId",
@@ -617,6 +634,11 @@ export const MODELS_BY_PROVIDER: Record<
617634
label: "MiniMax",
618635
models: Object.keys(minimaxModels),
619636
},
637+
mimo: {
638+
id: "mimo",
639+
label: "Xiaomi MiMo",
640+
models: Object.keys(mimoModels),
641+
},
620642
"openai-codex": {
621643
id: "openai-codex",
622644
label: "OpenAI - ChatGPT Plus/Pro",

packages/types/src/providers/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export * from "./xai.js"
2525
export * from "./vercel-ai-gateway.js"
2626
export * from "./zai.js"
2727
export * from "./minimax.js"
28+
export * from "./mimo.js"
2829

2930
import { anthropicDefaultModelId } from "./anthropic.js"
3031
import { basetenDefaultModelId } from "./baseten.js"
@@ -49,6 +50,7 @@ import { xaiDefaultModelId } from "./xai.js"
4950
import { vercelAiGatewayDefaultModelId } from "./vercel-ai-gateway.js"
5051
import { internationalZAiDefaultModelId, mainlandZAiDefaultModelId } from "./zai.js"
5152
import { minimaxDefaultModelId } from "./minimax.js"
53+
import { mimoDefaultModelId } from "./mimo.js"
5254

5355
// Import the ProviderName type from provider-settings to avoid duplication
5456
import type { ProviderName } from "../provider-settings.js"
@@ -85,6 +87,8 @@ export function getProviderDefaultModelId(
8587
return moonshotDefaultModelId
8688
case "minimax":
8789
return minimaxDefaultModelId
90+
case "mimo":
91+
return mimoDefaultModelId
8892
case "zai":
8993
return options?.isChina ? mainlandZAiDefaultModelId : internationalZAiDefaultModelId
9094
case "openai-native":
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import type { ModelInfo } from "../model.js"
2+
3+
// https://developer.puter.com/ai/xiaomi/mimo-v2.5-pro/
4+
// https://developer.puter.com/ai/xiaomi/mimo-v2.5/
5+
// https://platform.xiaomimimo.com/docs/en-US/quick-start/model-hyperparameters
6+
//
7+
// NOTE: mimo-v2-flash is not included here. Its thinking mode defaults to
8+
// disabled and it doesn't reliably handle reasoning_content passthrough
9+
// during multi-turn tool calling, which causes 400 errors from the proxy.
10+
// If flash support is needed later, it should be validated against the API
11+
// first — the tool-calling + thinking flow is what makes MiMo useful as
12+
// an agentic provider, and flash just can't do that yet.
13+
export type MimoModelId = keyof typeof mimoModels
14+
15+
export const mimoDefaultModelId: MimoModelId = "mimo-v2.5-pro"
16+
17+
export const mimoModels = {
18+
"mimo-v2.5-pro": {
19+
maxTokens: 131_072,
20+
contextWindow: 1_048_576,
21+
supportsImages: false, // Pro series is text-only
22+
supportsPromptCache: false,
23+
preserveReasoning: true,
24+
inputPrice: 1.0, // $1.00/1M tokens (cache miss, ≤256K)
25+
outputPrice: 3.0, // $3.00/1M tokens (≤256K)
26+
cacheReadsPrice: 0.2, // $0.20/1M tokens (cache hit, ≤256K)
27+
cacheWritesPrice: 0, // Free for limited time
28+
// MiMo charges 2x above 256K context
29+
longContextPricing: {
30+
thresholdTokens: 256_000,
31+
inputPriceMultiplier: 2,
32+
outputPriceMultiplier: 2,
33+
cacheReadsPriceMultiplier: 2,
34+
},
35+
description:
36+
"MiMo V2.5 Pro - Xiaomi's flagship reasoning model with 1M context, deep thinking, tool calling, and structured output.",
37+
},
38+
"mimo-v2.5": {
39+
maxTokens: 131_072,
40+
contextWindow: 1_048_576,
41+
supportsImages: true, // Full-modal: text, image, audio, video input
42+
supportsPromptCache: false,
43+
preserveReasoning: true,
44+
inputPrice: 0.4, // $0.40/1M tokens (cache miss, ≤256K)
45+
outputPrice: 2.0, // $2.00/1M tokens (≤256K)
46+
cacheReadsPrice: 0.08, // $0.08/1M tokens (cache hit, ≤256K)
47+
cacheWritesPrice: 0, // Free for limited time
48+
// MiMo charges 2x above 256K context
49+
longContextPricing: {
50+
thresholdTokens: 256_000,
51+
inputPriceMultiplier: 2,
52+
outputPriceMultiplier: 2,
53+
cacheReadsPriceMultiplier: 2,
54+
},
55+
description:
56+
"MiMo V2.5 - Full-modal understanding model (text, image, audio, video) with 1M context, deep thinking, tool calling, and structured output.",
57+
},
58+
} as const satisfies Record<string, ModelInfo>
59+
60+
export const mimoDefaultModelInfo: ModelInfo = mimoModels[mimoDefaultModelId]
61+
62+
export const MIMO_DEFAULT_TEMPERATURE = 1.0

src/api/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import {
3333
FireworksHandler,
3434
VercelAiGatewayHandler,
3535
MiniMaxHandler,
36+
MimoHandler,
3637
BasetenHandler,
3738
} from "./providers"
3839
import { NativeOllamaHandler } from "./providers/native-ollama"
@@ -167,6 +168,8 @@ export function buildApiHandler(configuration: ProviderSettings): ApiHandler {
167168
return new LiteLLMHandler(options)
168169
case "sambanova":
169170
return new SambaNovaHandler(options)
171+
case "mimo":
172+
return new MimoHandler(options)
170173
case "zai":
171174
return new ZAiHandler(options)
172175
case "fireworks":

0 commit comments

Comments
 (0)