feat(providers): add xiaomi direct API (token-plan-{ams,sgp,cn}) — additive

Adds direct xiaomi token-plan API access alongside the existing
OpenRouter-routed xiaomi entries. ADDITIVE only — OpenRouter cleanup is
a separate follow-up.

Three new region providers:
- xiaomi-token-plan-ams (Amsterdam, default for plain `xiaomi`)
- xiaomi-token-plan-sgp (Singapore)
- xiaomi-token-plan-cn (China)

All use Anthropic Messages API. Env-var resolution: XIAOMI_API_KEY →
XIAOMI_TOKEN_PLAN_API_KEY → MIMO_API_KEY (in that fallback order).

Three xiaomi MiMo models registered under each direct provider:
- mimo-v2-flash (256k ctx, 64k output, text-only, reasoning)
- mimo-v2-omni (256k ctx, 128k output, text+image, reasoning)
- mimo-v2-pro (1M ctx, 128k output, text-only, reasoning)

Same model literals × 4 provider keys, different baseUrls per region.
Test count assertion bumped 22 → 26 providers.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Mikael Hugo 2026-04-29 13:54:43 +02:00
parent dff0df5fdc
commit ae0bbe32fc
6 changed files with 260 additions and 3 deletions

View file

@ -18,6 +18,9 @@ const AUTH_ENV_KEYS = [
["openrouter", "OPENROUTER_API_KEY"],
["mistral", "MISTRAL_API_KEY"],
["xiaomi", "XIAOMI_API_KEY"],
["xiaomi-token-plan-ams", "XIAOMI_API_KEY"],
["xiaomi-token-plan-sgp", "XIAOMI_API_KEY"],
["xiaomi-token-plan-cn", "XIAOMI_API_KEY"],
["ollama-cloud", "OLLAMA_API_KEY"],
["custom-openai", "CUSTOM_OPENAI_API_KEY"],
["cerebras", "CEREBRAS_API_KEY"],

View file

@ -99,6 +99,16 @@ export function getEnvApiKey(provider: any): string | undefined {
}
}
// Xiaomi MiMo token-plan providers share a single key; allow legacy fallbacks.
if (
provider === "xiaomi" ||
provider === "xiaomi-token-plan-ams" ||
provider === "xiaomi-token-plan-sgp" ||
provider === "xiaomi-token-plan-cn"
) {
return process.env.XIAOMI_API_KEY || process.env.XIAOMI_TOKEN_PLAN_API_KEY || process.env.MIMO_API_KEY;
}
if (provider === "amazon-bedrock") {
// Amazon Bedrock supports multiple credential sources:
// 1. AWS_PROFILE - named profile from ~/.aws/credentials
@ -137,6 +147,9 @@ export function getEnvApiKey(provider: any): string | undefined {
"opencode-go": "OPENCODE_API_KEY",
"kimi-coding": "KIMI_API_KEY",
xiaomi: "XIAOMI_API_KEY",
"xiaomi-token-plan-ams": "XIAOMI_API_KEY",
"xiaomi-token-plan-sgp": "XIAOMI_API_KEY",
"xiaomi-token-plan-cn": "XIAOMI_API_KEY",
"alibaba-coding-plan": "ALIBABA_API_KEY",
"alibaba-dashscope": "DASHSCOPE_API_KEY",
ollama: "OLLAMA_API_KEY",

View file

@ -257,9 +257,9 @@ describe("MODELS structural invariants", () => {
// ═══════════════════════════════════════════════════════════════════════════
describe("MODELS registry shape", () => {
it("has exactly 22 providers", () => {
it("has exactly 26 providers", () => {
const count = Object.keys(MODELS).length;
assert.equal(count, 22, `Expected 22 providers, got ${count}: ${Object.keys(MODELS).join(", ")}`);
assert.equal(count, 26, `Expected 26 providers, got ${count}: ${Object.keys(MODELS).join(", ")}`);
});
it("has at least 200 models in total (sanity check)", () => {
@ -270,7 +270,7 @@ describe("MODELS registry shape", () => {
assert.ok(total >= 200, `Registry has only ${total} models — unexpectedly small`);
});
it("all 22 expected providers are present", () => {
it("all 26 expected providers are present", () => {
const expected = [
"amazon-bedrock",
"anthropic",
@ -293,6 +293,10 @@ describe("MODELS registry shape", () => {
"openrouter",
"vercel-ai-gateway",
"xai",
"xiaomi",
"xiaomi-token-plan-ams",
"xiaomi-token-plan-cn",
"xiaomi-token-plan-sgp",
"zai",
];
const actual = Object.keys(MODELS).sort();

View file

@ -14379,4 +14379,225 @@ export const MODELS = {
maxTokens: 131072,
} satisfies Model<"openai-completions">,
},
// ──────────────────────────────────────────────────────────────────────────
// Xiaomi MiMo direct token-plan providers (Anthropic Messages API).
// Additive to the existing OpenRouter-routed xiaomi entries above; users
// with a XIAOMI_API_KEY can hit these endpoints directly.
// - "xiaomi" → default region (Amsterdam)
// - "xiaomi-token-plan-ams" → Amsterdam (Europe)
// - "xiaomi-token-plan-sgp" → Singapore
// - "xiaomi-token-plan-cn" → China
// ──────────────────────────────────────────────────────────────────────────
"xiaomi": {
"mimo-v2-flash": {
id: "mimo-v2-flash",
name: "MiMo-V2-Flash",
api: "anthropic-messages",
provider: "xiaomi",
baseUrl: "https://token-plan-ams.xiaomimimo.com/anthropic",
reasoning: true,
input: ["text"],
cost: {
input: 0.1,
output: 0.3,
cacheRead: 0.01,
cacheWrite: 0,
},
contextWindow: 256000,
maxTokens: 64000,
} satisfies Model<"anthropic-messages">,
"mimo-v2-omni": {
id: "mimo-v2-omni",
name: "MiMo-V2-Omni",
api: "anthropic-messages",
provider: "xiaomi",
baseUrl: "https://token-plan-ams.xiaomimimo.com/anthropic",
reasoning: true,
input: ["text", "image"],
cost: {
input: 0.4,
output: 2,
cacheRead: 0.08,
cacheWrite: 0,
},
contextWindow: 256000,
maxTokens: 128000,
} satisfies Model<"anthropic-messages">,
"mimo-v2-pro": {
id: "mimo-v2-pro",
name: "MiMo-V2-Pro",
api: "anthropic-messages",
provider: "xiaomi",
baseUrl: "https://token-plan-ams.xiaomimimo.com/anthropic",
reasoning: true,
input: ["text"],
cost: {
input: 1,
output: 3,
cacheRead: 0.2,
cacheWrite: 0,
},
contextWindow: 1000000,
maxTokens: 128000,
} satisfies Model<"anthropic-messages">,
},
"xiaomi-token-plan-ams": {
"mimo-v2-flash": {
id: "mimo-v2-flash",
name: "MiMo-V2-Flash",
api: "anthropic-messages",
provider: "xiaomi-token-plan-ams",
baseUrl: "https://token-plan-ams.xiaomimimo.com/anthropic",
reasoning: true,
input: ["text"],
cost: {
input: 0.1,
output: 0.3,
cacheRead: 0.01,
cacheWrite: 0,
},
contextWindow: 256000,
maxTokens: 64000,
} satisfies Model<"anthropic-messages">,
"mimo-v2-omni": {
id: "mimo-v2-omni",
name: "MiMo-V2-Omni",
api: "anthropic-messages",
provider: "xiaomi-token-plan-ams",
baseUrl: "https://token-plan-ams.xiaomimimo.com/anthropic",
reasoning: true,
input: ["text", "image"],
cost: {
input: 0.4,
output: 2,
cacheRead: 0.08,
cacheWrite: 0,
},
contextWindow: 256000,
maxTokens: 128000,
} satisfies Model<"anthropic-messages">,
"mimo-v2-pro": {
id: "mimo-v2-pro",
name: "MiMo-V2-Pro",
api: "anthropic-messages",
provider: "xiaomi-token-plan-ams",
baseUrl: "https://token-plan-ams.xiaomimimo.com/anthropic",
reasoning: true,
input: ["text"],
cost: {
input: 1,
output: 3,
cacheRead: 0.2,
cacheWrite: 0,
},
contextWindow: 1000000,
maxTokens: 128000,
} satisfies Model<"anthropic-messages">,
},
"xiaomi-token-plan-sgp": {
"mimo-v2-flash": {
id: "mimo-v2-flash",
name: "MiMo-V2-Flash",
api: "anthropic-messages",
provider: "xiaomi-token-plan-sgp",
baseUrl: "https://token-plan-sgp.xiaomimimo.com/anthropic",
reasoning: true,
input: ["text"],
cost: {
input: 0.1,
output: 0.3,
cacheRead: 0.01,
cacheWrite: 0,
},
contextWindow: 256000,
maxTokens: 64000,
} satisfies Model<"anthropic-messages">,
"mimo-v2-omni": {
id: "mimo-v2-omni",
name: "MiMo-V2-Omni",
api: "anthropic-messages",
provider: "xiaomi-token-plan-sgp",
baseUrl: "https://token-plan-sgp.xiaomimimo.com/anthropic",
reasoning: true,
input: ["text", "image"],
cost: {
input: 0.4,
output: 2,
cacheRead: 0.08,
cacheWrite: 0,
},
contextWindow: 256000,
maxTokens: 128000,
} satisfies Model<"anthropic-messages">,
"mimo-v2-pro": {
id: "mimo-v2-pro",
name: "MiMo-V2-Pro",
api: "anthropic-messages",
provider: "xiaomi-token-plan-sgp",
baseUrl: "https://token-plan-sgp.xiaomimimo.com/anthropic",
reasoning: true,
input: ["text"],
cost: {
input: 1,
output: 3,
cacheRead: 0.2,
cacheWrite: 0,
},
contextWindow: 1000000,
maxTokens: 128000,
} satisfies Model<"anthropic-messages">,
},
"xiaomi-token-plan-cn": {
"mimo-v2-flash": {
id: "mimo-v2-flash",
name: "MiMo-V2-Flash",
api: "anthropic-messages",
provider: "xiaomi-token-plan-cn",
baseUrl: "https://token-plan-cn.xiaomimimo.com/anthropic",
reasoning: true,
input: ["text"],
cost: {
input: 0.1,
output: 0.3,
cacheRead: 0.01,
cacheWrite: 0,
},
contextWindow: 256000,
maxTokens: 64000,
} satisfies Model<"anthropic-messages">,
"mimo-v2-omni": {
id: "mimo-v2-omni",
name: "MiMo-V2-Omni",
api: "anthropic-messages",
provider: "xiaomi-token-plan-cn",
baseUrl: "https://token-plan-cn.xiaomimimo.com/anthropic",
reasoning: true,
input: ["text", "image"],
cost: {
input: 0.4,
output: 2,
cacheRead: 0.08,
cacheWrite: 0,
},
contextWindow: 256000,
maxTokens: 128000,
} satisfies Model<"anthropic-messages">,
"mimo-v2-pro": {
id: "mimo-v2-pro",
name: "MiMo-V2-Pro",
api: "anthropic-messages",
provider: "xiaomi-token-plan-cn",
baseUrl: "https://token-plan-cn.xiaomimimo.com/anthropic",
reasoning: true,
input: ["text"],
cost: {
input: 1,
output: 3,
cacheRead: 0.2,
cacheWrite: 0,
},
contextWindow: 1000000,
maxTokens: 128000,
} satisfies Model<"anthropic-messages">,
},
} as const;

View file

@ -43,6 +43,9 @@ export type KnownProvider =
| "opencode-go"
| "kimi-coding"
| "xiaomi"
| "xiaomi-token-plan-ams"
| "xiaomi-token-plan-sgp"
| "xiaomi-token-plan-cn"
| "alibaba-coding-plan"
| "alibaba-dashscope"
| "ollama"

View file

@ -47,6 +47,16 @@ export function getEnvApiKey(provider: string): string | undefined {
}
}
// Xiaomi MiMo token-plan providers share a single key; allow legacy fallbacks.
if (
provider === "xiaomi" ||
provider === "xiaomi-token-plan-ams" ||
provider === "xiaomi-token-plan-sgp" ||
provider === "xiaomi-token-plan-cn"
) {
return process.env.XIAOMI_API_KEY || process.env.XIAOMI_TOKEN_PLAN_API_KEY || process.env.MIMO_API_KEY;
}
if (
provider === "amazon-bedrock" &&
(
@ -79,6 +89,9 @@ export function getEnvApiKey(provider: string): string | undefined {
"opencode-go": "OPENCODE_API_KEY",
"kimi-coding": "KIMI_API_KEY",
xiaomi: "XIAOMI_API_KEY",
"xiaomi-token-plan-ams": "XIAOMI_API_KEY",
"xiaomi-token-plan-sgp": "XIAOMI_API_KEY",
"xiaomi-token-plan-cn": "XIAOMI_API_KEY",
"alibaba-coding-plan": "ALIBABA_API_KEY",
};