Skip to content

Commit 18483e4

Browse files
kevincodex1OpenClaude (zai-org-glm-5-1)jatmn
authored
feat(provider): add Xiaomi MiMo integration (#1152)
* feat(provider): add Xiaomi MiMo integration Add Xiaomi MiMo as an official OpenAI-compatible provider with provider-profile persistence, env-only detection, and sponsor labeling in the provider picker. Co-Authored-By: OpenClaude (zai-org-glm-5-1) <openclaude@gitlawb.com> * feat(provider): rebase Xiaomi MiMo as top-level provider Promote Xiaomi MiMo from generic OpenAI-compatible shim route to a first-class top-level provider matching the MiniMax pattern. Includes brand/model descriptors, env-only detection, provider auto-detect, status labels, legacy provider type, model picker, and profile env persistence. Co-Authored-By: OpenClaude (zai-org-glm-5-1) <openclaude@gitlawb.com> * Fix Xiaomi MiMo provider integration Promote Xiaomi MiMo as a first-tier OpenAI-compatible vendor and align its descriptor with the integration guide. Use the resolving Xiaomi MiMo API host while normalizing the stale docs host alias, wire Xiaomi catalog options into /model, and ensure model selection/display uses OPENAI_MODEL for Xiaomi instead of Claude defaults. Update provider profile/startup handling, OpenAI shim detection, docs, generated integration artifacts, and focused regression tests. Verification: bun run integrations:check; focused bun test provider/model suites; bun run build; bun run smoke. * Fix MiMo startup profile base URL normalization --------- Co-authored-by: OpenClaude (zai-org-glm-5-1) <openclaude@gitlawb.com> Co-authored-by: JATMN <the@jat.mn>
1 parent cac11dc commit 18483e4

33 files changed

Lines changed: 938 additions & 12 deletions

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ Advanced and source-build guides:
149149
| GitHub Models | `/onboard-github` | Interactive onboarding with saved credentials |
150150
| Codex OAuth | `/provider` | Opens ChatGPT sign-in in your browser and stores Codex credentials securely |
151151
| Codex | `/provider` | Uses existing Codex CLI auth, OpenClaude secure storage, or env credentials |
152+
| Xiaomi MiMo | `/provider` or env vars | OpenAI-compatible API at `https://api.xiaomimimo.com/v1`; uses `MIMO_API_KEY` and defaults to `mimo-v2.5-pro` |
152153
| Ollama | `/provider`, env vars, or `ollama launch` | Local inference with no API key |
153154
| Atomic Chat | `/provider`, env vars, or `bun run dev:atomic-chat` | Local Model Provider; auto-detects loaded models |
154155
| Bedrock / Vertex / Foundry | env vars | Additional provider integrations for supported environments |
@@ -170,6 +171,7 @@ OpenClaude supports multiple providers, but behavior is not identical across all
170171
- Tool quality depends heavily on the selected model
171172
- Smaller local models can struggle with long multi-step tool flows
172173
- Some providers impose lower output caps than the CLI defaults, and OpenClaude adapts where possible
174+
- Xiaomi MiMo uses `api-key` header auth on the OpenAI-compatible route and currently does not support `/usage` reporting in OpenClaude
173175

174176
For best results, use models with strong tool/function calling support.
175177

docs/advanced-setup.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,17 @@ export OPENAI_MODEL=llama-3.3-70b-versatile
163163

164164
`GROQ_API_KEY` matches the built-in Groq gateway preset. `OPENAI_API_KEY` also works as a fallback on the generic OpenAI-compatible path, but `GROQ_API_KEY` is the preferred variable for Groq-specific setup.
165165

166+
### Xiaomi MiMo
167+
168+
```bash
169+
export CLAUDE_CODE_USE_OPENAI=1
170+
export MIMO_API_KEY=...
171+
export OPENAI_BASE_URL=https://api.xiaomimimo.com/v1
172+
export OPENAI_MODEL=mimo-v2.5-pro
173+
```
174+
175+
The `/provider` Xiaomi MiMo preset uses the same endpoint and stores the key as `MIMO_API_KEY`. `OPENAI_API_KEY` also works as a compatibility fallback, but `MIMO_API_KEY` keeps the profile tied to the MiMo route.
176+
166177
### Mistral
167178

168179
```bash
@@ -189,6 +200,7 @@ export OPENAI_MODEL=gpt-4o
189200
| `OPENAI_MODEL` | OpenAI-compatible only | Model name such as `gpt-4o`, `deepseek-v4-flash`, or `llama3.3:70b` |
190201
| `OPENAI_BASE_URL` | No | API endpoint, defaulting to `https://api.openai.com/v1` |
191202
| `OPENAI_API_BASE` | No | Compatibility alias for `OPENAI_BASE_URL` |
203+
| `MIMO_API_KEY` | Xiaomi MiMo route | Xiaomi MiMo API key for `https://api.xiaomimimo.com/v1`; mirrored into the OpenAI-compatible auth env when the MiMo route is active |
192204
| `CLAUDE_CODE_USE_GEMINI` | Gemini only | Set to `1` to enable the direct Gemini provider path |
193205
| `GEMINI_API_KEY` / `GOOGLE_API_KEY` | Gemini API-key auth | Gemini API key for direct Gemini setup |
194206
| `GEMINI_MODEL` | Gemini only | Model name such as `gemini-3-flash-preview` or `gemini-2.5-pro` |

src/components/ProviderManager.test.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ const PRESET_ORDER = [
128128
'Together AI',
129129
'Venice',
130130
'xAI',
131+
'Xiaomi MiMo',
131132
'Z.AI - GLM Coding Plan',
132133
'Custom',
133134
] as const

src/components/ProviderManager.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,19 @@ function toDraft(profile: ProviderProfile): ProviderDraft {
218218
}
219219
}
220220

221+
function getPresetLabel(preset: ProviderPreset, label: string): React.ReactNode {
222+
if (preset !== 'xiaomi-mimo') {
223+
return label
224+
}
225+
226+
return (
227+
<Text>
228+
<Text>{label} </Text>
229+
<Text color="success" bold>[Sponsor]</Text>
230+
</Text>
231+
)
232+
}
233+
221234
function presetToDraft(preset: ProviderPreset): ProviderDraft {
222235
const defaults = getProviderPresetDefaults(preset)
223236
return {
@@ -1517,7 +1530,7 @@ export function ProviderManager({ mode, onDone }: Props): React.ReactNode {
15171530
const metadata = getProviderPresetUiMetadata(preset)
15181531
return {
15191532
value: preset,
1520-
label: metadata.label,
1533+
label: getPresetLabel(preset, metadata.label),
15211534
description: metadata.description,
15221535
}
15231536
})
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { defineBrand } from '../define.js'
2+
3+
export default defineBrand({
4+
id: 'xiaomi-mimo',
5+
label: 'Xiaomi MiMo',
6+
canonicalVendorId: 'xiaomi-mimo',
7+
defaultCapabilities: {
8+
supportsStreaming: true,
9+
supportsFunctionCalling: true,
10+
supportsJsonMode: true,
11+
supportsReasoning: true,
12+
supportsPreciseTokenCount: false,
13+
},
14+
modelIds: [
15+
'mimo-v2.5-pro',
16+
'mimo-v2-pro',
17+
'mimo-v2.5',
18+
'mimo-v2-omni',
19+
'mimo-v2-flash',
20+
],
21+
})

src/integrations/compatibility.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ const EXPECTED_PRESETS = [
3636
'minimax',
3737
'xai',
3838
'venice',
39+
'xiaomi-mimo',
3940
'zai',
4041
'bankr',
4142
'atomic-chat',

src/integrations/generated/integrationArtifacts.generated.ts

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import vendorMoonshot from '../vendors/moonshot.js'
1111
import vendorOpenai from '../vendors/openai.js'
1212
import vendorVenice from '../vendors/venice.js'
1313
import vendorXai from '../vendors/xai.js'
14+
import vendorXiaomiMimo from '../vendors/xiaomi-mimo.js'
1415
import vendorZai from '../vendors/zai.js'
1516
import gatewayAtomicChat from '../gateways/atomic-chat.js'
1617
import gatewayAzureOpenai from '../gateways/azure-openai.js'
@@ -42,6 +43,7 @@ import brandNemotron from '../brands/nemotron.js'
4243
import brandOpenaiCompatibleAlias from '../brands/openai-compatible-alias.js'
4344
import brandQwen from '../brands/qwen.js'
4445
import brandXai from '../brands/xai.js'
46+
import brandXiaomiMimo from '../brands/xiaomi-mimo.js'
4547
import modelClaude from '../models/claude.js'
4648
import modelDeepseek from '../models/deepseek.js'
4749
import modelGemini from '../models/gemini.js'
@@ -55,12 +57,13 @@ import modelNemotron from '../models/nemotron.js'
5557
import modelOpenaiCompatibleAlias from '../models/openai-compatible-alias.js'
5658
import modelQwen from '../models/qwen.js'
5759
import modelXai from '../models/xai.js'
60+
import modelXiaomiMimo from '../models/xiaomi-mimo.js'
5861

59-
export const VENDOR_DESCRIPTORS = [vendorAnthropic, vendorBankr, vendorDeepseek, vendorGemini, vendorMinimax, vendorMoonshot, vendorOpenai, vendorVenice, vendorXai, vendorZai] as const satisfies readonly VendorDescriptor[]
62+
export const VENDOR_DESCRIPTORS = [vendorAnthropic, vendorBankr, vendorDeepseek, vendorGemini, vendorMinimax, vendorMoonshot, vendorOpenai, vendorVenice, vendorXai, vendorXiaomiMimo, vendorZai] as const satisfies readonly VendorDescriptor[]
6063
export const GATEWAY_DESCRIPTORS = [gatewayAtomicChat, gatewayAzureOpenai, gatewayBedrock, gatewayCustom, gatewayDashscopeCn, gatewayDashscopeIntl, gatewayGithub, gatewayGroq, gatewayHicap, gatewayKimiCode, gatewayLmstudio, gatewayMistral, gatewayNvidiaNim, gatewayOllama, gatewayOpenrouter, gatewayTogether, gatewayVertex] as const satisfies readonly GatewayDescriptor[]
6164
export const ANTHROPIC_PROXY_DESCRIPTORS = [] as const satisfies readonly AnthropicProxyDescriptor[]
62-
export const BRAND_DESCRIPTORS = [brandClaude, brandDeepseek, brandGemini, brandGlm, brandGpt, brandKimi, brandLlama, brandMinimax, brandMistral, brandNemotron, brandOpenaiCompatibleAlias, brandQwen, brandXai] as const satisfies readonly BrandDescriptor[]
63-
export const MODEL_DESCRIPTOR_GROUPS = [modelClaude, modelDeepseek, modelGemini, modelGlm, modelGpt, modelKimi, modelLlama, modelMinimax, modelMistral, modelNemotron, modelOpenaiCompatibleAlias, modelQwen, modelXai] as const satisfies readonly (readonly ModelDescriptor[])[]
65+
export const BRAND_DESCRIPTORS = [brandClaude, brandDeepseek, brandGemini, brandGlm, brandGpt, brandKimi, brandLlama, brandMinimax, brandMistral, brandNemotron, brandOpenaiCompatibleAlias, brandQwen, brandXai, brandXiaomiMimo] as const satisfies readonly BrandDescriptor[]
66+
export const MODEL_DESCRIPTOR_GROUPS = [modelClaude, modelDeepseek, modelGemini, modelGlm, modelGpt, modelKimi, modelLlama, modelMinimax, modelMistral, modelNemotron, modelOpenaiCompatibleAlias, modelQwen, modelXai, modelXiaomiMimo] as const satisfies readonly (readonly ModelDescriptor[])[]
6467
export const MODEL_DESCRIPTORS = MODEL_DESCRIPTOR_GROUPS.flat() satisfies readonly ModelDescriptor[]
6568

6669
export const PROVIDER_PRESET_MANIFEST = [
@@ -318,6 +321,21 @@ export const PROVIDER_PRESET_MANIFEST = [
318321
"OPENAI_MODEL"
319322
]
320323
},
324+
{
325+
"preset": "xiaomi-mimo",
326+
"routeKind": "vendor",
327+
"routeId": "xiaomi-mimo",
328+
"vendorId": "xiaomi-mimo",
329+
"description": "Xiaomi MiMo OpenAI-compatible endpoint",
330+
"label": "Xiaomi MiMo",
331+
"name": "Xiaomi MiMo",
332+
"apiKeyEnvVars": [
333+
"MIMO_API_KEY"
334+
],
335+
"modelEnvVars": [
336+
"OPENAI_MODEL"
337+
]
338+
},
321339
{
322340
"preset": "zai",
323341
"routeKind": "vendor",
@@ -379,6 +397,7 @@ export const ORDERED_PROVIDER_PRESETS = [
379397
"together",
380398
"venice",
381399
"xai",
400+
"xiaomi-mimo",
382401
"zai",
383402
"custom"
384403
] as const

src/integrations/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ export {
113113
getRouteLabel,
114114
getRouteProviderTypeLabel,
115115
getTransportKindForRoute,
116+
normalizeXiaomiMimoBaseUrl,
116117
resolveActiveRouteIdFromEnv,
117118
resolveRouteIdFromBaseUrl,
118119
routeSupportsApiFormatSelection,
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { defineModel } from '../define.js'
2+
3+
const proCapabilities = {
4+
supportsStreaming: true,
5+
supportsFunctionCalling: true,
6+
supportsJsonMode: true,
7+
supportsReasoning: true,
8+
supportsPreciseTokenCount: false,
9+
}
10+
11+
const omniCapabilities = {
12+
supportsStreaming: true,
13+
supportsFunctionCalling: true,
14+
supportsJsonMode: true,
15+
supportsReasoning: true,
16+
supportsVision: true,
17+
supportsPreciseTokenCount: false,
18+
}
19+
20+
export default [
21+
defineModel({
22+
id: 'mimo-v2.5-pro',
23+
label: 'MiMo V2.5 Pro',
24+
brandId: 'xiaomi-mimo',
25+
vendorId: 'xiaomi-mimo',
26+
classification: ['chat', 'reasoning', 'coding'],
27+
defaultModel: 'mimo-v2.5-pro',
28+
capabilities: proCapabilities,
29+
contextWindow: 1_000_000,
30+
maxOutputTokens: 128_000,
31+
}),
32+
defineModel({
33+
id: 'mimo-v2-pro',
34+
label: 'MiMo V2 Pro',
35+
brandId: 'xiaomi-mimo',
36+
vendorId: 'xiaomi-mimo',
37+
classification: ['chat', 'reasoning', 'coding'],
38+
defaultModel: 'mimo-v2-pro',
39+
capabilities: proCapabilities,
40+
contextWindow: 1_000_000,
41+
maxOutputTokens: 128_000,
42+
}),
43+
defineModel({
44+
id: 'mimo-v2.5',
45+
label: 'MiMo V2.5',
46+
brandId: 'xiaomi-mimo',
47+
vendorId: 'xiaomi-mimo',
48+
classification: ['chat', 'reasoning', 'vision', 'coding'],
49+
defaultModel: 'mimo-v2.5',
50+
capabilities: omniCapabilities,
51+
contextWindow: 1_000_000,
52+
maxOutputTokens: 128_000,
53+
}),
54+
defineModel({
55+
id: 'mimo-v2-omni',
56+
label: 'MiMo V2 Omni',
57+
brandId: 'xiaomi-mimo',
58+
vendorId: 'xiaomi-mimo',
59+
classification: ['chat', 'reasoning', 'vision', 'coding'],
60+
defaultModel: 'mimo-v2-omni',
61+
capabilities: omniCapabilities,
62+
contextWindow: 256_000,
63+
maxOutputTokens: 128_000,
64+
}),
65+
defineModel({
66+
id: 'mimo-v2-flash',
67+
label: 'MiMo V2 Flash',
68+
brandId: 'xiaomi-mimo',
69+
vendorId: 'xiaomi-mimo',
70+
classification: ['chat', 'reasoning', 'coding'],
71+
defaultModel: 'mimo-v2-flash',
72+
capabilities: proCapabilities,
73+
contextWindow: 256_000,
74+
maxOutputTokens: 64_000,
75+
}),
76+
]

src/integrations/routeMetadata.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ test('getRouteCredentialEnvVars keeps descriptor env vars and openai fallback fo
4848
'VENICE_API_KEY',
4949
'OPENAI_API_KEY',
5050
])
51+
expect(getRouteCredentialEnvVars('xiaomi-mimo')).toEqual([
52+
'MIMO_API_KEY',
53+
'OPENAI_API_KEY',
54+
])
5155
})
5256

5357
test('getRouteCredentialValue reads the first configured route credential', () => {
@@ -70,6 +74,22 @@ test('Venice route metadata uses official OpenAI-compatible defaults', () => {
7074
expect(resolveRouteIdFromBaseUrl('https://api.venice.ai/api/v1/chat/completions')).toBe('venice')
7175
})
7276

77+
test('Xiaomi MiMo route metadata uses official OpenAI-compatible defaults', () => {
78+
expect(getRouteDefaultBaseUrl('xiaomi-mimo')).toBe('https://api.xiaomimimo.com/v1')
79+
expect(getRouteDefaultModel('xiaomi-mimo')).toBe('mimo-v2.5-pro')
80+
expect(resolveRouteIdFromBaseUrl('https://api.xiaomimimo.com/v1')).toBe('xiaomi-mimo')
81+
expect(resolveRouteIdFromBaseUrl('https://api.xiaomimimo.com/v1/chat/completions')).toBe('xiaomi-mimo')
82+
expect(resolveRouteIdFromBaseUrl('https://api.mimo-v2.com/v1')).toBe('xiaomi-mimo')
83+
})
84+
85+
test('resolveActiveRouteIdFromEnv treats Xiaomi MiMo credential-only env as Xiaomi MiMo', () => {
86+
expect(
87+
resolveActiveRouteIdFromEnv({
88+
MIMO_API_KEY: 'mimo-key',
89+
}),
90+
).toBe('xiaomi-mimo')
91+
})
92+
7393
test('resolveActiveRouteIdFromEnv treats MiniMax credential-only env as MiniMax', () => {
7494
expect(
7595
resolveActiveRouteIdFromEnv({
@@ -129,6 +149,7 @@ test.each([
129149
['OpenRouter', 'https://openrouter.ai/api/v1', 'openai/gpt-5-mini', 'openrouter'],
130150
['DeepSeek', 'https://api.deepseek.com/v1', 'deepseek-v4-pro', 'deepseek'],
131151
['Hicap', 'https://api.hicap.ai/v1', 'claude-opus-4.7', 'hicap'],
152+
['Xiaomi MiMo', 'https://api.xiaomimimo.com/v1', 'mimo-v2.5-pro', 'xiaomi-mimo'],
132153
['Venice', 'https://api.venice.ai/api/v1', 'venice-uncensored', 'venice'],
133154
])(
134155
'resolveActiveRouteIdFromEnv refines generic OpenAI profile by %s base URL',

0 commit comments

Comments
 (0)