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:
parent
dff0df5fdc
commit
ae0bbe32fc
6 changed files with 260 additions and 3 deletions
|
|
@ -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"],
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue