feat: add Xiaomi MiMo provider onboarding (#3454)

Thanks @WqyJh.

Co-authored-by: Qiying Wang <15232241+WqyJh@users.noreply.github.com>
This commit is contained in:
Peter Steinberger 2026-01-29 17:29:58 +00:00
parent 50d44d0bd9
commit 78b9876641
5 changed files with 61 additions and 3 deletions

View File

@ -15,6 +15,7 @@ Status: beta.
### Changes ### Changes
- Providers: add Venice AI integration; update Moonshot Kimi references to kimi-k2.5; update MiniMax API endpoint/format. (#2762, #3064) - Providers: add Venice AI integration; update Moonshot Kimi references to kimi-k2.5; update MiniMax API endpoint/format. (#2762, #3064)
- Providers: add Xiaomi MiMo (mimo-v2-flash) support and onboarding flow. (#3454) Thanks @WqyJh.
- Telegram: quote replies, edit-message action, silent sends, sticker support + vision caching, linkPreview toggle, plugin sendPayload support. (#2900, #2394, #2382, #2548, #1700, #1917) - Telegram: quote replies, edit-message action, silent sends, sticker support + vision caching, linkPreview toggle, plugin sendPayload support. (#2900, #2394, #2382, #2548, #1700, #1917)
- Discord: configurable privileged gateway intents for presences/members. (#2266) Thanks @kentaro. - Discord: configurable privileged gateway intents for presences/members. (#2266) Thanks @kentaro.
- Browser: route browser control via gateway/node; fallback URL matching for relay targets. (#1999) - Browser: route browser control via gateway/node; fallback URL matching for relay targets. (#1999)

View File

@ -31,7 +31,7 @@ const MINIMAX_API_COST = {
}; };
const XIAOMI_BASE_URL = "https://api.xiaomimimo.com/anthropic"; const XIAOMI_BASE_URL = "https://api.xiaomimimo.com/anthropic";
const XIAOMI_DEFAULT_MODEL_ID = "mimo-v2-flash"; export const XIAOMI_DEFAULT_MODEL_ID = "mimo-v2-flash";
const XIAOMI_DEFAULT_CONTEXT_WINDOW = 262144; const XIAOMI_DEFAULT_CONTEXT_WINDOW = 262144;
const XIAOMI_DEFAULT_MAX_TOKENS = 8192; const XIAOMI_DEFAULT_MAX_TOKENS = 8192;
const XIAOMI_DEFAULT_COST = { const XIAOMI_DEFAULT_COST = {

View File

@ -53,6 +53,7 @@ describe("models-config", () => {
const previousMoonshot = process.env.MOONSHOT_API_KEY; const previousMoonshot = process.env.MOONSHOT_API_KEY;
const previousSynthetic = process.env.SYNTHETIC_API_KEY; const previousSynthetic = process.env.SYNTHETIC_API_KEY;
const previousVenice = process.env.VENICE_API_KEY; const previousVenice = process.env.VENICE_API_KEY;
const previousXiaomi = process.env.XIAOMI_API_KEY;
delete process.env.COPILOT_GITHUB_TOKEN; delete process.env.COPILOT_GITHUB_TOKEN;
delete process.env.GH_TOKEN; delete process.env.GH_TOKEN;
delete process.env.GITHUB_TOKEN; delete process.env.GITHUB_TOKEN;
@ -61,6 +62,7 @@ describe("models-config", () => {
delete process.env.MOONSHOT_API_KEY; delete process.env.MOONSHOT_API_KEY;
delete process.env.SYNTHETIC_API_KEY; delete process.env.SYNTHETIC_API_KEY;
delete process.env.VENICE_API_KEY; delete process.env.VENICE_API_KEY;
delete process.env.XIAOMI_API_KEY;
try { try {
vi.resetModules(); vi.resetModules();
@ -93,6 +95,8 @@ describe("models-config", () => {
else process.env.SYNTHETIC_API_KEY = previousSynthetic; else process.env.SYNTHETIC_API_KEY = previousSynthetic;
if (previousVenice === undefined) delete process.env.VENICE_API_KEY; if (previousVenice === undefined) delete process.env.VENICE_API_KEY;
else process.env.VENICE_API_KEY = previousVenice; else process.env.VENICE_API_KEY = previousVenice;
if (previousXiaomi === undefined) delete process.env.XIAOMI_API_KEY;
else process.env.XIAOMI_API_KEY = previousXiaomi;
} }
}); });
}); });

View File

@ -1,4 +1,4 @@
import { buildXiaomiProvider } from "../agents/models-config.providers.js"; import { buildXiaomiProvider, XIAOMI_DEFAULT_MODEL_ID } from "../agents/models-config.providers.js";
import { import {
buildSyntheticModelDefinition, buildSyntheticModelDefinition,
SYNTHETIC_BASE_URL, SYNTHETIC_BASE_URL,
@ -349,7 +349,14 @@ export function applyXiaomiProviderConfig(cfg: MoltbotConfig): MoltbotConfig {
const existingProvider = providers.xiaomi; const existingProvider = providers.xiaomi;
const defaultProvider = buildXiaomiProvider(); const defaultProvider = buildXiaomiProvider();
const existingModels = Array.isArray(existingProvider?.models) ? existingProvider.models : []; const existingModels = Array.isArray(existingProvider?.models) ? existingProvider.models : [];
const mergedModels = existingModels.length > 0 ? existingModels : (defaultProvider.models ?? []); const defaultModels = defaultProvider.models ?? [];
const hasDefaultModel = existingModels.some((model) => model.id === XIAOMI_DEFAULT_MODEL_ID);
const mergedModels =
existingModels.length > 0
? hasDefaultModel
? existingModels
: [...existingModels, ...defaultModels]
: defaultModels;
const { apiKey: existingApiKey, ...existingProviderRest } = (existingProvider ?? {}) as Record< const { apiKey: existingApiKey, ...existingProviderRest } = (existingProvider ?? {}) as Record<
string, string,
unknown unknown

View File

@ -15,6 +15,8 @@ import {
applyOpenrouterProviderConfig, applyOpenrouterProviderConfig,
applySyntheticConfig, applySyntheticConfig,
applySyntheticProviderConfig, applySyntheticProviderConfig,
applyXiaomiConfig,
applyXiaomiProviderConfig,
OPENROUTER_DEFAULT_MODEL_REF, OPENROUTER_DEFAULT_MODEL_REF,
SYNTHETIC_DEFAULT_MODEL_ID, SYNTHETIC_DEFAULT_MODEL_ID,
SYNTHETIC_DEFAULT_MODEL_REF, SYNTHETIC_DEFAULT_MODEL_REF,
@ -343,6 +345,50 @@ describe("applySyntheticConfig", () => {
}); });
}); });
describe("applyXiaomiConfig", () => {
it("adds Xiaomi provider with correct settings", () => {
const cfg = applyXiaomiConfig({});
expect(cfg.models?.providers?.xiaomi).toMatchObject({
baseUrl: "https://api.xiaomimimo.com/anthropic",
api: "anthropic-messages",
});
expect(cfg.agents?.defaults?.model?.primary).toBe("xiaomi/mimo-v2-flash");
});
it("merges Xiaomi models and keeps existing provider overrides", () => {
const cfg = applyXiaomiProviderConfig({
models: {
providers: {
xiaomi: {
baseUrl: "https://old.example.com",
apiKey: "old-key",
api: "openai-completions",
models: [
{
id: "custom-model",
name: "Custom",
reasoning: false,
input: ["text"],
cost: { input: 1, output: 2, cacheRead: 0, cacheWrite: 0 },
contextWindow: 1000,
maxTokens: 100,
},
],
},
},
},
});
expect(cfg.models?.providers?.xiaomi?.baseUrl).toBe("https://api.xiaomimimo.com/anthropic");
expect(cfg.models?.providers?.xiaomi?.api).toBe("anthropic-messages");
expect(cfg.models?.providers?.xiaomi?.apiKey).toBe("old-key");
expect(cfg.models?.providers?.xiaomi?.models.map((m) => m.id)).toEqual([
"custom-model",
"mimo-v2-flash",
]);
});
});
describe("applyOpencodeZenProviderConfig", () => { describe("applyOpencodeZenProviderConfig", () => {
it("adds allowlist entry for the default model", () => { it("adds allowlist entry for the default model", () => {
const cfg = applyOpencodeZenProviderConfig({}); const cfg = applyOpencodeZenProviderConfig({});