diff --git a/.env.example b/.env.example index 19535567d5..d6119b0ed5 100644 --- a/.env.example +++ b/.env.example @@ -170,6 +170,10 @@ LLM_GROQ_API_KEY=your_groq_key_here # DeepSeek LLM_DEEPSEEK_API_KEY=your_deepseek_key_here +# Xiaomi MiMo +LLM_XIAOMI_API_KEY=your_xiaomi_key_here +# LLM_XIAOMI_BASE_URL=https://api.xiaomimimo.com + # Perplexity LLM_PERPLEXITY_API_KEY=your_perplexity_key_here diff --git a/.env.unified.example b/.env.unified.example index df67dcffab..4215d7e285 100644 --- a/.env.unified.example +++ b/.env.unified.example @@ -83,6 +83,10 @@ LLM_GROQ_API_KEY=your_groq_key_here # DeepSeek LLM_DEEPSEEK_API_KEY=your_deepseek_key_here +# Xiaomi MiMo +LLM_XIAOMI_API_KEY=your_xiaomi_key_here +# LLM_XIAOMI_BASE_URL=https://api.xiaomimimo.com + # Perplexity LLM_PERPLEXITY_API_KEY=your_perplexity_key_here diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 5751a397ae..8960f14588 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -83,6 +83,7 @@ jobs: LLM_EMBERCLOUD_API_KEY: ${{ secrets.LLM_EMBERCLOUD_API_KEY }} LLM_ALIBABA_API_KEY__US_VIRGINIA: ${{ secrets.LLM_ALIBABA_API_KEY__US_VIRGINIA }} LLM_ALIBABA_API_KEY__CN_BEIJING: ${{ secrets.LLM_ALIBABA_API_KEY__CN_BEIJING }} + LLM_XIAOMI_API_KEY: ${{ secrets.LLM_XIAOMI_API_KEY }} - name: Upload shard results if: always() diff --git a/apps/gateway/src/chat/tools/transform-streaming-to-openai.ts b/apps/gateway/src/chat/tools/transform-streaming-to-openai.ts index 13ccc5d21c..b24ebe36d0 100644 --- a/apps/gateway/src/chat/tools/transform-streaming-to-openai.ts +++ b/apps/gateway/src/chat/tools/transform-streaming-to-openai.ts @@ -1304,6 +1304,7 @@ export function transformStreamingToOpenai( case "bytedance": case "minimax": case "embercloud": + case "xiaomi": case "azure-ai-foundry": case "llmgateway": { // Azure AI Foundry mirrors Azure OpenAI's prompt-filter-only leading diff --git a/apps/ui/src/components/provider-keys/provider-logo.ts b/apps/ui/src/components/provider-keys/provider-logo.ts index 200d750707..c0d3fea6d7 100644 --- a/apps/ui/src/components/provider-keys/provider-logo.ts +++ b/apps/ui/src/components/provider-keys/provider-logo.ts @@ -26,7 +26,12 @@ export const providerLogoUrls: Partial< nanogpt: ProviderIcons.nanogpt, "aws-bedrock": ProviderIcons["aws-bedrock"], azure: ProviderIcons.azure, + "azure-ai-foundry": ProviderIcons["azure-ai-foundry"], cerebras: ProviderIcons.cerebras, + minimax: ProviderIcons.minimax, + bytedance: ProviderIcons.bytedance, + xiaomi: ProviderIcons.xiaomi, + embercloud: ProviderIcons.embercloud, }; export const getProviderLogoDarkModeClasses = () => { diff --git a/packages/actions/src/get-provider-endpoint.spec.ts b/packages/actions/src/get-provider-endpoint.spec.ts index b780a7bbab..80858d86b7 100644 --- a/packages/actions/src/get-provider-endpoint.spec.ts +++ b/packages/actions/src/get-provider-endpoint.spec.ts @@ -10,6 +10,7 @@ const originalVertexRegion = process.env.LLM_GOOGLE_VERTEX_REGION; const originalAzureFoundryResource = process.env.LLM_AZURE_AI_FOUNDRY_RESOURCE; const originalAzureFoundryApiVersion = process.env.LLM_AZURE_AI_FOUNDRY_API_VERSION; +const originalXiaomiBaseUrl = process.env.LLM_XIAOMI_BASE_URL; afterEach(() => { if (originalAiStudioBaseUrl === undefined) { @@ -54,6 +55,12 @@ afterEach(() => { process.env.LLM_AZURE_AI_FOUNDRY_API_VERSION = originalAzureFoundryApiVersion; } + + if (originalXiaomiBaseUrl === undefined) { + delete process.env.LLM_XIAOMI_BASE_URL; + } else { + process.env.LLM_XIAOMI_BASE_URL = originalXiaomiBaseUrl; + } }); describe("getProviderEndpoint", () => { @@ -249,6 +256,46 @@ describe("getProviderEndpoint", () => { }); }); + describe("xiaomi", () => { + it("builds the default Xiaomi endpoint", () => { + delete process.env.LLM_XIAOMI_BASE_URL; + + const endpoint = getProviderEndpoint( + "xiaomi", + undefined, + "mimo-v2.5-pro", + ); + + expect(endpoint).toBe("https://api.xiaomimimo.com/v1/chat/completions"); + }); + + it("uses custom base URL when provided", () => { + const endpoint = getProviderEndpoint( + "xiaomi", + "https://custom-xiaomi.example.com", + "mimo-v2-flash", + ); + + expect(endpoint).toBe( + "https://custom-xiaomi.example.com/v1/chat/completions", + ); + }); + + it("builds streaming endpoint", () => { + delete process.env.LLM_XIAOMI_BASE_URL; + + const endpoint = getProviderEndpoint( + "xiaomi", + undefined, + "mimo-v2.5", + undefined, + true, + ); + + expect(endpoint).toBe("https://api.xiaomimimo.com/v1/chat/completions"); + }); + }); + describe("skipEnvVars (BYOK mode)", () => { it("uses hardcoded default for google-ai-studio instead of env var", () => { process.env.LLM_GOOGLE_AI_STUDIO_BASE_URL = diff --git a/packages/actions/src/get-provider-endpoint.ts b/packages/actions/src/get-provider-endpoint.ts index 3e953d13ff..c7cf9c9adb 100644 --- a/packages/actions/src/get-provider-endpoint.ts +++ b/packages/actions/src/get-provider-endpoint.ts @@ -228,6 +228,14 @@ export function getProviderEndpoint( case "minimax": url = "https://api.minimax.io"; break; + case "xiaomi": + url = + envValueOrDefault( + "xiaomi", + "baseUrl", + "https://api.xiaomimimo.com", + ) ?? "https://api.xiaomimimo.com"; + break; case "aws-bedrock": url = envValueOrDefault( @@ -487,6 +495,7 @@ export function getProviderEndpoint( case "nebius": case "nanogpt": case "minimax": + case "xiaomi": case "embercloud": case "custom": default: diff --git a/packages/models/src/models.ts b/packages/models/src/models.ts index 93dddbc5ff..4f669f905f 100644 --- a/packages/models/src/models.ts +++ b/packages/models/src/models.ts @@ -13,6 +13,7 @@ import { nousresearchModels } from "./models/nousresearch.js"; import { openaiModels } from "./models/openai.js"; import { perplexityModels } from "./models/perplexity.js"; import { xaiModels } from "./models/xai.js"; +import { xiaomiModels } from "./models/xiaomi.js"; import { zaiModels } from "./models/zai.js"; import type { providers } from "./providers.js"; @@ -433,6 +434,7 @@ export const models = [ ...googleModels, ...perplexityModels, ...xaiModels, + ...xiaomiModels, ...metaModels, ...deepseekModels, ...mistralModels, diff --git a/packages/models/src/models/xiaomi.ts b/packages/models/src/models/xiaomi.ts new file mode 100644 index 0000000000..7e31fb06a6 --- /dev/null +++ b/packages/models/src/models/xiaomi.ts @@ -0,0 +1,179 @@ +import type { ModelDefinition } from "@/models.js"; + +export const xiaomiModels = [ + { + id: "mimo-v2.5-pro", + name: "MiMo V2.5 Pro", + description: + "Xiaomi's flagship 1T-parameter model with 42B activations, 1M ultra-long context, and deep thinking capabilities. Performs comparably to Claude Opus 4.6 in agent scenarios.", + family: "xiaomi", + releasedAt: new Date("2026-04-23"), + providers: [ + { + providerId: "xiaomi" as const, + modelName: "mimo-v2.5-pro", + inputPrice: 1 / 1e6, + outputPrice: 3 / 1e6, + cachedInputPrice: 0.2 / 1e6, + requestPrice: 0, + contextSize: 1000000, + maxOutput: 131072, + streaming: true, + reasoning: true, + vision: false, + tools: true, + jsonOutput: true, + pricingTiers: [ + { + name: "256K", + upToTokens: 256000, + inputPrice: 1 / 1e6, + outputPrice: 3 / 1e6, + cachedInputPrice: 0.2 / 1e6, + }, + { + name: "1M", + upToTokens: 1000000, + inputPrice: 2 / 1e6, + outputPrice: 6 / 1e6, + cachedInputPrice: 0.4 / 1e6, + }, + ], + }, + ], + }, + { + id: "mimo-v2-pro", + name: "MiMo V2 Pro", + description: + "Xiaomi's 1T-parameter model with 42B active parameters and 1M context window using hybrid Global Attention + SWA architecture.", + family: "xiaomi", + releasedAt: new Date("2026-03-18"), + providers: [ + { + providerId: "xiaomi" as const, + modelName: "mimo-v2-pro", + inputPrice: 1 / 1e6, + outputPrice: 3 / 1e6, + cachedInputPrice: 0.2 / 1e6, + requestPrice: 0, + contextSize: 1000000, + maxOutput: 131072, + streaming: true, + reasoning: true, + vision: false, + tools: true, + jsonOutput: true, + pricingTiers: [ + { + name: "256K", + upToTokens: 256000, + inputPrice: 1 / 1e6, + outputPrice: 3 / 1e6, + cachedInputPrice: 0.2 / 1e6, + }, + { + name: "1M", + upToTokens: 1000000, + inputPrice: 2 / 1e6, + outputPrice: 6 / 1e6, + cachedInputPrice: 0.4 / 1e6, + }, + ], + }, + ], + }, + { + id: "mimo-v2.5", + name: "MiMo V2.5", + description: + "Xiaomi's full-modal perception model supporting native understanding of images, videos, audio, and text with 1M context. Agent performance comparable to MiMo V2.5 Pro.", + family: "xiaomi", + releasedAt: new Date("2026-04-23"), + providers: [ + { + providerId: "xiaomi" as const, + modelName: "mimo-v2.5", + inputPrice: 0.4 / 1e6, + outputPrice: 2 / 1e6, + cachedInputPrice: 0.08 / 1e6, + requestPrice: 0, + contextSize: 1000000, + maxOutput: 131072, + streaming: true, + reasoning: true, + vision: true, + tools: true, + jsonOutput: true, + pricingTiers: [ + { + name: "256K", + upToTokens: 256000, + inputPrice: 0.4 / 1e6, + outputPrice: 2 / 1e6, + cachedInputPrice: 0.08 / 1e6, + }, + { + name: "1M", + upToTokens: 1000000, + inputPrice: 0.8 / 1e6, + outputPrice: 4 / 1e6, + cachedInputPrice: 0.16 / 1e6, + }, + ], + }, + ], + }, + { + id: "mimo-v2-omni", + name: "MiMo V2 Omni", + description: + "Xiaomi's multimodal model supporting text, vision, and speech modalities with 256K context window.", + family: "xiaomi", + releasedAt: new Date("2026-03-18"), + providers: [ + { + providerId: "xiaomi" as const, + modelName: "mimo-v2-omni", + inputPrice: 0.4 / 1e6, + outputPrice: 2 / 1e6, + cachedInputPrice: 0.08 / 1e6, + requestPrice: 0, + contextSize: 256000, + maxOutput: 131072, + streaming: true, + reasoning: false, + vision: true, + tools: true, + jsonOutput: true, + }, + ], + }, + { + id: "mimo-v2-flash", + name: "MiMo V2 Flash", + description: + "Xiaomi's high-efficiency inference model with hybrid architecture, 3 MTP layers for 2.5-3.7x faster inference, and 256K context.", + family: "xiaomi", + releasedAt: new Date("2025-12-16"), + providers: [ + { + providerId: "xiaomi" as const, + modelName: "mimo-v2-flash", + inputPrice: 0.1 / 1e6, + outputPrice: 0.3 / 1e6, + // Flash model uses a 10% cache discount (not 20% like other MiMo models) + cachedInputPrice: 0.01 / 1e6, + requestPrice: 0, + contextSize: 256000, + maxOutput: undefined, + streaming: true, + reasoning: true, + reasoningOutput: "omit" as const, + vision: false, + tools: true, + jsonOutput: true, + }, + ], + }, +] as const satisfies ModelDefinition[]; diff --git a/packages/models/src/providers.ts b/packages/models/src/providers.ts index 5d5d86b19d..e200ad340d 100644 --- a/packages/models/src/providers.ts +++ b/packages/models/src/providers.ts @@ -566,6 +566,25 @@ export const providers = [ website: "https://www.embercloud.ai", announcement: null, }, + { + id: "xiaomi", + name: "Xiaomi", + description: + "Xiaomi MiMo API Open Platform provides access to the MiMo series of large language models.", + env: { + required: { + apiKey: "LLM_XIAOMI_API_KEY", + }, + optional: { + baseUrl: "LLM_XIAOMI_BASE_URL", + }, + }, + streaming: true, + cancellation: true, + color: "#FF6900", + website: "https://platform.xiaomimimo.com", + announcement: null, + }, ] as const satisfies ProviderDefinition[]; export type ProviderId = (typeof providers)[number]["id"]; diff --git a/packages/shared/src/components/provider-icons.tsx b/packages/shared/src/components/provider-icons.tsx index eebcff98d9..0b7efd5865 100644 --- a/packages/shared/src/components/provider-icons.tsx +++ b/packages/shared/src/components/provider-icons.tsx @@ -979,6 +979,22 @@ export const MinimaxIcon: React.FC> = (props) => { ); }; +// Xiaomi Icon +export const XiaomiIcon: React.FC> = (props) => ( + + + + +); + export const EmberCloudIcon: React.FC> = ( props, ) => ( @@ -1026,6 +1042,7 @@ export const ProviderIcons = { "azure-ai-foundry": AzureIcon, cerebras: CerebrasIcon, minimax: MinimaxIcon, + xiaomi: XiaomiIcon, embercloud: EmberCloudIcon, } as const; @@ -1060,6 +1077,7 @@ export const providerLogoUrls: Partial< "azure-ai-foundry": ProviderIcons["azure-ai-foundry"], cerebras: ProviderIcons.cerebras, minimax: ProviderIcons.minimax, + xiaomi: ProviderIcons.xiaomi, embercloud: ProviderIcons.embercloud, };