fix: auto-detect China region for MiniMax API endpoint selection

This commit is contained in:
SK Akram 2026-01-30 15:43:17 +00:00
parent da71eaebd2
commit b163440f6f
6 changed files with 124 additions and 9 deletions

View File

@ -145,13 +145,50 @@ Use the interactive config wizard to set MiniMax without editing JSON:
## Configuration options
- `models.providers.minimax.baseUrl`: prefer `https://api.minimax.io/anthropic` (Anthropic-compatible); `https://api.minimax.io/v1` is optional for OpenAI-compatible payloads.
- `models.providers.minimax.baseUrl`: auto-detected based on timezone:
- China mainland/HK/TW: `https://api.minimaxi.com/anthropic` (domestic)
- Overseas: `https://api.minimax.io/anthropic` (default)
- For OpenAI-compatible payloads, replace `/anthropic` with `/v1`
- `models.providers.minimax.api`: prefer `anthropic-messages`; `openai-completions` is optional for OpenAI-compatible payloads.
- `models.providers.minimax.apiKey`: MiniMax API key (`MINIMAX_API_KEY`).
- `models.providers.minimax.models`: define `id`, `name`, `reasoning`, `contextWindow`, `maxTokens`, `cost`.
- `agents.defaults.models`: alias models you want in the allowlist.
- `models.mode`: keep `merge` if you want to add MiniMax alongside built-ins.
## China Mainland Users
OpenClaw automatically detects China mainland timezones (Asia/Shanghai, Asia/Hong_Kong,
Asia/Taipei, etc.) and uses the domestic endpoint (`api.minimaxi.com`) for faster,
more reliable connections.
> [!NOTE]
> The domain difference is subtle: `minimaxi.com` (with extra 'i') vs `minimax.io`.
To manually override the baseUrl (e.g., if timezone detection doesn't work for your setup):
```bash
openclaw config set models.providers.minimax.baseUrl "https://api.minimaxi.com/anthropic"
```
Or edit `openclaw.json` directly:
```json5
{
models: {
providers: {
minimax: {
baseUrl: "https://api.minimaxi.com/anthropic",
apiKey: "${MINIMAX_API_KEY}",
api: "anthropic-messages",
models: [...]
}
}
}
}
```
Then restart the gateway with `openclaw gateway restart`.
## Notes
- Model refs are `minimax/<model>`.

View File

@ -1,3 +1,5 @@
import { isChinaRegion } from "../infra/region-utils.js";
type MinimaxBaseResp = {
status_code?: number;
status_msg?: string;
@ -9,11 +11,12 @@ function coerceApiHost(params: {
env?: NodeJS.ProcessEnv;
}): string {
const env = params.env ?? process.env;
const defaultHost = isChinaRegion() ? "https://api.minimaxi.com" : "https://api.minimax.io";
const raw =
params.apiHost?.trim() ||
env.MINIMAX_API_HOST?.trim() ||
params.modelBaseUrl?.trim() ||
"https://api.minimax.io";
defaultHost;
try {
const url = new URL(raw);
@ -24,7 +27,7 @@ function coerceApiHost(params: {
const url = new URL(`https://${raw}`);
return url.origin;
} catch {
return "https://api.minimax.io";
return defaultHost;
}
}

View File

@ -13,11 +13,14 @@ import {
SYNTHETIC_MODEL_CATALOG,
} from "./synthetic-models.js";
import { discoverVeniceModels, VENICE_BASE_URL } from "./venice-models.js";
import { isChinaRegion } from "../infra/region-utils.js";
type ModelsConfig = NonNullable<OpenClawConfig["models"]>;
export type ProviderConfig = NonNullable<ModelsConfig["providers"]>[string];
const MINIMAX_API_BASE_URL = "https://api.minimax.chat/v1";
function getMinimaxImplicitBaseUrl(): string {
return isChinaRegion() ? "https://api.minimaxi.com/v1" : "https://api.minimax.chat/v1";
}
const MINIMAX_DEFAULT_MODEL_ID = "MiniMax-M2.1";
const MINIMAX_DEFAULT_VISION_MODEL_ID = "MiniMax-VL-01";
const MINIMAX_DEFAULT_CONTEXT_WINDOW = 200000;
@ -254,7 +257,7 @@ export function normalizeProviders(params: {
function buildMinimaxProvider(): ProviderConfig {
return {
baseUrl: MINIMAX_API_BASE_URL,
baseUrl: getMinimaxImplicitBaseUrl(),
api: "openai-completions",
models: [
{

View File

@ -1,11 +1,11 @@
import type { OpenClawConfig } from "../config/config.js";
import { isChinaRegion } from "../infra/region-utils.js";
import {
buildMinimaxApiModelDefinition,
buildMinimaxModelDefinition,
DEFAULT_MINIMAX_BASE_URL,
DEFAULT_MINIMAX_CONTEXT_WINDOW,
DEFAULT_MINIMAX_MAX_TOKENS,
MINIMAX_API_BASE_URL,
getMinimaxBaseUrl,
MINIMAX_HOSTED_COST,
MINIMAX_HOSTED_MODEL_ID,
MINIMAX_HOSTED_MODEL_REF,
@ -81,7 +81,7 @@ export function applyMinimaxHostedProviderConfig(
const mergedModels = hasHostedModel ? existingModels : [...existingModels, hostedModel];
providers.minimax = {
...existingProvider,
baseUrl: params?.baseUrl?.trim() || DEFAULT_MINIMAX_BASE_URL,
baseUrl: params?.baseUrl?.trim() || getMinimaxBaseUrl(isChinaRegion(), "v1"),
apiKey: "minimax",
api: "openai-completions",
models: mergedModels.length > 0 ? mergedModels : [hostedModel],
@ -164,7 +164,7 @@ export function applyMinimaxApiProviderConfig(
const normalizedApiKey = resolvedApiKey?.trim() === "minimax" ? "" : resolvedApiKey;
providers.minimax = {
...existingProviderRest,
baseUrl: MINIMAX_API_BASE_URL,
baseUrl: getMinimaxBaseUrl(isChinaRegion(), "anthropic"),
api: "anthropic-messages",
...(normalizedApiKey?.trim() ? { apiKey: normalizedApiKey } : {}),
models: mergedModels.length > 0 ? mergedModels : [apiModel],

View File

@ -2,6 +2,26 @@ import type { ModelDefinitionConfig } from "../config/types.js";
export const DEFAULT_MINIMAX_BASE_URL = "https://api.minimax.io/v1";
export const MINIMAX_API_BASE_URL = "https://api.minimax.io/anthropic";
export const MINIMAX_CHINA_BASE_URL = "https://api.minimaxi.com/v1";
export const MINIMAX_CHINA_API_BASE_URL = "https://api.minimaxi.com/anthropic";
/**
* Returns the appropriate MiniMax base URL based on region.
*
* @param isChinaRegion - Whether the user is in China/Greater China
* @param api - Which API variant to use: "v1" for OpenAI-compatible, "anthropic" for Anthropic-compatible
* @returns The appropriate base URL for the region and API variant
*/
export function getMinimaxBaseUrl(
isChinaRegion: boolean,
api: "v1" | "anthropic" = "anthropic",
): string {
if (isChinaRegion) {
return api === "v1" ? MINIMAX_CHINA_BASE_URL : MINIMAX_CHINA_API_BASE_URL;
}
return api === "v1" ? DEFAULT_MINIMAX_BASE_URL : MINIMAX_API_BASE_URL;
}
export const MINIMAX_HOSTED_MODEL_ID = "MiniMax-M2.1";
export const MINIMAX_HOSTED_MODEL_REF = `minimax/${MINIMAX_HOSTED_MODEL_ID}`;
export const DEFAULT_MINIMAX_CONTEXT_WINDOW = 200000;

52
src/infra/region-utils.ts Normal file
View File

@ -0,0 +1,52 @@
/**
* Region detection utilities for provider endpoint selection.
*
* Some providers (e.g., MiniMax) have region-specific endpoints. This module
* provides timezone-based region detection to automatically select the
* appropriate endpoint for users in different regions.
*/
/**
* Timezone identifiers for China and Greater China regions.
* These timezones indicate the user is likely in mainland China, Hong Kong,
* Macau, or Taiwan and may benefit from using domestic API endpoints.
*/
const CHINA_TIMEZONES = new Set([
"Asia/Shanghai",
"Asia/Chongqing",
"Asia/Harbin",
"Asia/Urumqi",
"PRC",
"Asia/Hong_Kong",
"Hongkong",
"Asia/Macau",
"Asia/Taipei",
"ROC",
]);
/**
* Detects if the current environment is likely in China or Greater China
* based on the system timezone.
*
* @returns `true` if the timezone indicates China/Greater China region
*
* @example
* ```ts
* if (isChinaRegion()) {
* // Use domestic API endpoint
* baseUrl = "https://api.minimaxi.com/anthropic";
* } else {
* // Use overseas API endpoint
* baseUrl = "https://api.minimax.io/anthropic";
* }
* ```
*/
export function isChinaRegion(): boolean {
try {
const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
return CHINA_TIMEZONES.has(tz);
} catch {
// If timezone detection fails, default to overseas
return false;
}
}