diff --git a/packages/ai/src/provider-models/openai-compat.ts b/packages/ai/src/provider-models/openai-compat.ts index 0ce002db4..b23eb3d88 100644 --- a/packages/ai/src/provider-models/openai-compat.ts +++ b/packages/ai/src/provider-models/openai-compat.ts @@ -1733,58 +1733,51 @@ export function xiaomiModelManagerOptions( ): ModelManagerOptions<"openai-completions"> { const apiKey = config?.apiKey; // Xiaomi splits API keys across two backends: standard `sk-` keys hit - // api.xiaomimimo.com; "token plan" `tp-` keys hit either the SG or EU - // token-plan host. Try SGP first; if discovery fails, retry AMS. - const TOKEN_PLAN_SGP_BASE_URL = "https://token-plan-sgp.xiaomimimo.com/v1"; - const TOKEN_PLAN_AMS_BASE_URL = "https://token-plan-ams.xiaomimimo.com/v1"; - const defaultBaseUrl = apiKey?.startsWith("tp-") ? TOKEN_PLAN_SGP_BASE_URL : "https://api.xiaomimimo.com/v1"; - // Token-plan keys always use the TP baseUrl; config?.baseUrl (from catalog) + // api.xiaomimimo.com; "token plan" `tp-` keys are scoped to a regional + // cluster and are tried in order until discovery succeeds. + const TOKEN_PLAN_BASE_URLS = [ + "https://token-plan-sgp.xiaomimimo.com/v1", + "https://token-plan-ams.xiaomimimo.com/v1", + "https://token-plan-cn.xiaomimimo.com/v1", + ] as const; + const STANDARD_BASE_URL = "https://api.xiaomimimo.com/v1"; + const isTokenPlanKey = apiKey?.startsWith("tp-"); + // Token-plan keys always use a TP cluster; config?.baseUrl (from catalog) // would incorrectly pin to the standard endpoint (api.xiaomimimo.com). - const baseUrl = apiKey?.startsWith("tp-") ? defaultBaseUrl : (config?.baseUrl ?? defaultBaseUrl); + const baseUrl = isTokenPlanKey ? TOKEN_PLAN_BASE_URLS[0] : (config?.baseUrl ?? STANDARD_BASE_URL); const references = createBundledReferenceMap<"openai-completions">("xiaomi"); + const fetchModels = (url: string) => + fetchOpenAICompatibleModels({ + api: "openai-completions", + provider: "xiaomi", + baseUrl: url, + apiKey, + filterModel: (_entry, model) => !model.id.includes("-tts"), + mapModel: (entry, defaults) => { + const reference = references.get(defaults.id); + const model = mapWithBundledReference(entry, defaults, reference); + return { + ...model, + name: toModelName(entry.display_name, model.name), + }; + }, + }); return { providerId: "xiaomi", ...(apiKey && { fetchDynamicModels: async () => { - const sgpResult = await fetchOpenAICompatibleModels({ - api: "openai-completions", - provider: "xiaomi", - baseUrl, - apiKey, - filterModel: (_entry, model) => !model.id.includes("-tts"), - mapModel: (entry, defaults) => { - const reference = references.get(defaults.id); - const model = mapWithBundledReference(entry, defaults, reference); - return { - ...model, - name: toModelName(entry.display_name, model.name), - }; - }, - }); - if (sgpResult || !apiKey?.startsWith("tp-")) { - return sgpResult; + if (!isTokenPlanKey) { + return fetchModels(baseUrl); } - // Token-plan discovery failed with SGP; retry with AMS - return fetchOpenAICompatibleModels({ - api: "openai-completions", - provider: "xiaomi", - baseUrl: TOKEN_PLAN_AMS_BASE_URL, - apiKey, - filterModel: (_entry, model) => !model.id.includes("-tts"), - mapModel: (entry, defaults) => { - const reference = references.get(defaults.id); - const model = mapWithBundledReference(entry, defaults, reference); - return { - ...model, - name: toModelName(entry.display_name, model.name), - }; - }, - }); + for (const url of TOKEN_PLAN_BASE_URLS) { + const result = await fetchModels(url); + if (result) return result; + } + return null; }, }), }; } - // --------------------------------------------------------------------------- // 21. LiteLLM // ---------------------------------------------------------------------------