From 031252f1305b0d19a51b4e9738b2ecb078017813 Mon Sep 17 00:00:00 2001 From: octo-patch <266937838+octo-patch@users.noreply.github.com> Date: Fri, 31 Jul 2026 18:50:34 +0800 Subject: [PATCH] Add MiniMax provider support --- src/entrypoints/sdk/coreSchemas.ts | 2 +- src/utils/context.ts | 9 +++++++++ src/utils/model/configs.ts | 16 ++++++++++++++++ src/utils/model/deprecation.ts | 3 +++ src/utils/model/model.ts | 15 +++++++++++++++ src/utils/model/modelOptions.ts | 21 +++++++++++++++++++++ src/utils/model/providers.ts | 30 ++++++++++++++++++++++++++++-- src/utils/status.tsx | 11 ++++++++++- src/utils/thinking.ts | 18 +++++++++++++++++- 9 files changed, 120 insertions(+), 5 deletions(-) diff --git a/src/entrypoints/sdk/coreSchemas.ts b/src/entrypoints/sdk/coreSchemas.ts index 4d5b9d0a0..4ef8ea5a0 100644 --- a/src/entrypoints/sdk/coreSchemas.ts +++ b/src/entrypoints/sdk/coreSchemas.ts @@ -1087,7 +1087,7 @@ export const AccountInfoSchema = lazySchema(() => tokenSource: z.string().optional(), apiKeySource: z.string().optional(), apiProvider: z - .enum(['firstParty', 'bedrock', 'vertex', 'foundry']) + .enum(['firstParty', 'bedrock', 'vertex', 'foundry', 'minimax']) .optional() .describe( 'Active API backend. Anthropic OAuth login only applies when "firstParty"; for 3P providers the other fields are absent and auth is external (AWS creds, gcloud ADC, etc.).', diff --git a/src/utils/context.ts b/src/utils/context.ts index d9714de9a..b358a5fe3 100644 --- a/src/utils/context.ts +++ b/src/utils/context.ts @@ -1,6 +1,7 @@ // biome-ignore-all assist/source/organizeImports: ANT-ONLY import markers must not be reordered import { CONTEXT_1M_BETA_HEADER } from '../constants/betas.js' import { getGlobalConfig } from './config.js' +import { MINIMAX_MODEL_CONFIGS } from './model/configs.js' import { isEnvTruthy } from './envUtils.js' import { getCanonicalName } from './model/model.js' import { getModelCapability } from './model/modelCapabilities.js' @@ -71,6 +72,14 @@ export function getContextWindowForModel( return 1_000_000 } + const normalizedModel = model.toLowerCase() + if (normalizedModel === MINIMAX_MODEL_CONFIGS.m3.toLowerCase()) { + return 1_000_000 + } + if (normalizedModel === MINIMAX_MODEL_CONFIGS.m27.toLowerCase()) { + return 204_800 + } + const cap = getModelCapability(model) if (cap?.max_input_tokens && cap.max_input_tokens >= 100_000) { if ( diff --git a/src/utils/model/configs.ts b/src/utils/model/configs.ts index 89f243d87..0c6a07876 100644 --- a/src/utils/model/configs.ts +++ b/src/utils/model/configs.ts @@ -3,6 +3,11 @@ import type { APIProvider } from './providers.js' export type ModelConfig = Record +export const MINIMAX_MODEL_CONFIGS = { + m3: 'MiniMax-M3', + m27: 'MiniMax-M2.7', +} as const satisfies Record + // @[MODEL LAUNCH]: Add a new CLAUDE_*_CONFIG constant here. Double check the correct model strings // here since the pattern may change. @@ -11,6 +16,7 @@ export const CLAUDE_3_7_SONNET_CONFIG = { bedrock: 'us.anthropic.claude-3-7-sonnet-20250219-v1:0', vertex: 'claude-3-7-sonnet@20250219', foundry: 'claude-3-7-sonnet', + minimax: MINIMAX_MODEL_CONFIGS.m3, } as const satisfies ModelConfig export const CLAUDE_3_5_V2_SONNET_CONFIG = { @@ -18,6 +24,7 @@ export const CLAUDE_3_5_V2_SONNET_CONFIG = { bedrock: 'anthropic.claude-3-5-sonnet-20241022-v2:0', vertex: 'claude-3-5-sonnet-v2@20241022', foundry: 'claude-3-5-sonnet', + minimax: MINIMAX_MODEL_CONFIGS.m3, } as const satisfies ModelConfig export const CLAUDE_3_5_HAIKU_CONFIG = { @@ -25,6 +32,7 @@ export const CLAUDE_3_5_HAIKU_CONFIG = { bedrock: 'us.anthropic.claude-3-5-haiku-20241022-v1:0', vertex: 'claude-3-5-haiku@20241022', foundry: 'claude-3-5-haiku', + minimax: MINIMAX_MODEL_CONFIGS.m3, } as const satisfies ModelConfig export const CLAUDE_HAIKU_4_5_CONFIG = { @@ -32,6 +40,7 @@ export const CLAUDE_HAIKU_4_5_CONFIG = { bedrock: 'us.anthropic.claude-haiku-4-5-20251001-v1:0', vertex: 'claude-haiku-4-5@20251001', foundry: 'claude-haiku-4-5', + minimax: MINIMAX_MODEL_CONFIGS.m3, } as const satisfies ModelConfig export const CLAUDE_SONNET_4_CONFIG = { @@ -39,6 +48,7 @@ export const CLAUDE_SONNET_4_CONFIG = { bedrock: 'us.anthropic.claude-sonnet-4-20250514-v1:0', vertex: 'claude-sonnet-4@20250514', foundry: 'claude-sonnet-4', + minimax: MINIMAX_MODEL_CONFIGS.m3, } as const satisfies ModelConfig export const CLAUDE_SONNET_4_5_CONFIG = { @@ -46,6 +56,7 @@ export const CLAUDE_SONNET_4_5_CONFIG = { bedrock: 'us.anthropic.claude-sonnet-4-5-20250929-v1:0', vertex: 'claude-sonnet-4-5@20250929', foundry: 'claude-sonnet-4-5', + minimax: MINIMAX_MODEL_CONFIGS.m3, } as const satisfies ModelConfig export const CLAUDE_OPUS_4_CONFIG = { @@ -53,6 +64,7 @@ export const CLAUDE_OPUS_4_CONFIG = { bedrock: 'us.anthropic.claude-opus-4-20250514-v1:0', vertex: 'claude-opus-4@20250514', foundry: 'claude-opus-4', + minimax: MINIMAX_MODEL_CONFIGS.m3, } as const satisfies ModelConfig export const CLAUDE_OPUS_4_1_CONFIG = { @@ -60,6 +72,7 @@ export const CLAUDE_OPUS_4_1_CONFIG = { bedrock: 'us.anthropic.claude-opus-4-1-20250805-v1:0', vertex: 'claude-opus-4-1@20250805', foundry: 'claude-opus-4-1', + minimax: MINIMAX_MODEL_CONFIGS.m3, } as const satisfies ModelConfig export const CLAUDE_OPUS_4_5_CONFIG = { @@ -67,6 +80,7 @@ export const CLAUDE_OPUS_4_5_CONFIG = { bedrock: 'us.anthropic.claude-opus-4-5-20251101-v1:0', vertex: 'claude-opus-4-5@20251101', foundry: 'claude-opus-4-5', + minimax: MINIMAX_MODEL_CONFIGS.m3, } as const satisfies ModelConfig export const CLAUDE_OPUS_4_6_CONFIG = { @@ -74,6 +88,7 @@ export const CLAUDE_OPUS_4_6_CONFIG = { bedrock: 'us.anthropic.claude-opus-4-6-v1', vertex: 'claude-opus-4-6', foundry: 'claude-opus-4-6', + minimax: MINIMAX_MODEL_CONFIGS.m3, } as const satisfies ModelConfig export const CLAUDE_SONNET_4_6_CONFIG = { @@ -81,6 +96,7 @@ export const CLAUDE_SONNET_4_6_CONFIG = { bedrock: 'us.anthropic.claude-sonnet-4-6', vertex: 'claude-sonnet-4-6', foundry: 'claude-sonnet-4-6', + minimax: MINIMAX_MODEL_CONFIGS.m3, } as const satisfies ModelConfig // @[MODEL LAUNCH]: Register the new config here. diff --git a/src/utils/model/deprecation.ts b/src/utils/model/deprecation.ts index a8b0ee231..3b0601064 100644 --- a/src/utils/model/deprecation.ts +++ b/src/utils/model/deprecation.ts @@ -38,6 +38,7 @@ const DEPRECATED_MODELS: Record = { bedrock: 'January 15, 2026', vertex: 'January 5, 2026', foundry: 'January 5, 2026', + minimax: null, }, }, 'claude-3-7-sonnet': { @@ -47,6 +48,7 @@ const DEPRECATED_MODELS: Record = { bedrock: 'April 28, 2026', vertex: 'May 11, 2026', foundry: 'February 19, 2026', + minimax: null, }, }, 'claude-3-5-haiku': { @@ -56,6 +58,7 @@ const DEPRECATED_MODELS: Record = { bedrock: null, vertex: null, foundry: null, + minimax: null, }, }, } diff --git a/src/utils/model/model.ts b/src/utils/model/model.ts index 85e369fa8..5e2cabafb 100644 --- a/src/utils/model/model.ts +++ b/src/utils/model/model.ts @@ -20,6 +20,7 @@ import { } from '../context.js' import { isEnvTruthy } from '../envUtils.js' import { getModelStrings, resolveOverriddenModel } from './modelStrings.js' +import { MINIMAX_MODEL_CONFIGS } from './configs.js' import { formatModelPricing, getOpus46CostTier } from '../modelCost.js' import { getSettings_DEPRECATED } from '../settings/settings.js' import type { PermissionMode } from '../permissions/PermissionMode.js' @@ -348,6 +349,12 @@ export function renderModelSetting(setting: ModelName | ModelAlias): string { */ export function getPublicModelDisplayName(model: ModelName): string | null { switch (model) { + case MINIMAX_MODEL_CONFIGS.m3: + return 'MiniMax M3' + case MINIMAX_MODEL_CONFIGS.m3 + '[1m]': + return 'MiniMax M3 (1M context)' + case MINIMAX_MODEL_CONFIGS.m27: + return 'MiniMax M2.7' case getModelStrings().opus46: return 'Opus 4.6' case getModelStrings().opus46 + '[1m]': @@ -575,6 +582,14 @@ export function getMarketingNameForModel(modelId: string): string | undefined { const has1m = modelId.toLowerCase().includes('[1m]') const canonical = getCanonicalName(modelId) + const normalizedModel = normalizeModelStringForAPI(modelId).toLowerCase() + + if (normalizedModel === MINIMAX_MODEL_CONFIGS.m3.toLowerCase()) { + return has1m ? 'MiniMax M3 (with 1M context)' : 'MiniMax M3' + } + if (normalizedModel === MINIMAX_MODEL_CONFIGS.m27.toLowerCase()) { + return 'MiniMax M2.7' + } if (canonical.includes('claude-opus-4-6')) { return has1m ? 'Opus 4.6 (with 1M context)' : 'Opus 4.6' diff --git a/src/utils/model/modelOptions.ts b/src/utils/model/modelOptions.ts index f8ef2966a..60f067e79 100644 --- a/src/utils/model/modelOptions.ts +++ b/src/utils/model/modelOptions.ts @@ -14,6 +14,7 @@ import { } from '../modelCost.js' import { getSettings_DEPRECATED } from '../settings/settings.js' import { checkOpus1mAccess, checkSonnet1mAccess } from './check1mAccess.js' +import { MINIMAX_MODEL_CONFIGS } from './configs.js' import { getAPIProvider } from './providers.js' import { isModelAllowed } from './modelAllowlist.js' import { @@ -266,6 +267,22 @@ function getOpusPlanOption(): ModelOption { } } +function getMiniMaxModelOptions(): ModelOption[] { + return [ + getDefaultOptionForUser(), + { + value: MINIMAX_MODEL_CONFIGS.m3, + label: 'MiniMax M3', + description: 'MiniMax M3 - 1M context - text, image, and video input', + }, + { + value: MINIMAX_MODEL_CONFIGS.m27, + label: 'MiniMax M2.7', + description: 'MiniMax M2.7 - 204.8K context - text input', + }, + ] +} + // @[MODEL LAUNCH]: Update the model picker lists below to include/reorder options for the new model. // Each user tier (ant, Max/Team Premium, Pro/Team Standard/Enterprise, PAYG 1P, PAYG 3P) has its own list. function getModelOptionsBase(fastMode = false): ModelOption[] { @@ -287,6 +304,10 @@ function getModelOptionsBase(fastMode = false): ModelOption[] { ] } + if (getAPIProvider() === 'minimax') { + return getMiniMaxModelOptions() + } + if (isClaudeAISubscriber()) { if (isMaxSubscriber() || isTeamPremiumSubscriber()) { // Max and Team Premium users: Opus is default, show Sonnet as alternative diff --git a/src/utils/model/providers.ts b/src/utils/model/providers.ts index aba9b7d7f..2f49546e6 100644 --- a/src/utils/model/providers.ts +++ b/src/utils/model/providers.ts @@ -1,7 +1,31 @@ import type { AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS } from '../../services/analytics/index.js' import { isEnvTruthy } from '../envUtils.js' -export type APIProvider = 'firstParty' | 'bedrock' | 'vertex' | 'foundry' +export type APIProvider = + | 'firstParty' + | 'bedrock' + | 'vertex' + | 'foundry' + | 'minimax' + +const MINIMAX_ANTHROPIC_BASE_URLS = new Set([ + 'https://api.minimax.io/anthropic', + 'https://api.minimaxi.com/anthropic', +]) + +export function isMiniMaxAnthropicBaseUrl(): boolean { + const baseUrl = process.env.ANTHROPIC_BASE_URL + if (!baseUrl) { + return false + } + try { + const url = new URL(baseUrl) + const normalizedUrl = `${url.protocol}//${url.host}${url.pathname.replace(/\/+$/, '')}` + return MINIMAX_ANTHROPIC_BASE_URLS.has(normalizedUrl) + } catch { + return false + } +} export function getAPIProvider(): APIProvider { return isEnvTruthy(process.env.CLAUDE_CODE_USE_BEDROCK) @@ -10,7 +34,9 @@ export function getAPIProvider(): APIProvider { ? 'vertex' : isEnvTruthy(process.env.CLAUDE_CODE_USE_FOUNDRY) ? 'foundry' - : 'firstParty' + : isMiniMaxAnthropicBaseUrl() + ? 'minimax' + : 'firstParty' } export function getAPIProviderForStatsig(): AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS { diff --git a/src/utils/status.tsx b/src/utils/status.tsx index 2cc221b53..db42b6b74 100644 --- a/src/utils/status.tsx +++ b/src/utils/status.tsx @@ -244,7 +244,8 @@ export function buildAPIProviderProperties(): Property[] { const providerLabel = { bedrock: 'AWS Bedrock', vertex: 'Google Vertex AI', - foundry: 'Microsoft Foundry' + foundry: 'Microsoft Foundry', + minimax: 'MiniMax' }[apiProvider]; properties.push({ label: 'API provider', @@ -259,6 +260,14 @@ export function buildAPIProviderProperties(): Property[] { value: anthropicBaseUrl }); } + } else if (apiProvider === 'minimax') { + const minimaxBaseUrl = process.env.ANTHROPIC_BASE_URL; + if (minimaxBaseUrl) { + properties.push({ + label: 'MiniMax base URL', + value: minimaxBaseUrl + }); + } } else if (apiProvider === 'bedrock') { const bedrockBaseUrl = process.env.BEDROCK_BASE_URL; if (bedrockBaseUrl) { diff --git a/src/utils/thinking.ts b/src/utils/thinking.ts index b37fe42de..aeaddd710 100644 --- a/src/utils/thinking.ts +++ b/src/utils/thinking.ts @@ -6,6 +6,7 @@ import { getCanonicalName } from './model/model.js' import { get3PModelCapabilityOverride } from './model/modelSupportOverrides.js' import { getAPIProvider } from './model/providers.js' import { getSettingsWithErrors } from './settings/settings.js' +import { MINIMAX_MODEL_CONFIGS } from './model/configs.js' export type ThinkingConfig = | { type: 'adaptive' } @@ -101,6 +102,15 @@ export function modelSupportsThinking(model: string): boolean { // launch DRI and research. This can greatly affect model quality and bashing. const canonical = getCanonicalName(model) const provider = getAPIProvider() + if (provider === 'minimax') { + const normalizedModel = model + .replace(/\[1m\]$/i, '') + .toLowerCase() + return ( + normalizedModel === MINIMAX_MODEL_CONFIGS.m3.toLowerCase() || + normalizedModel === MINIMAX_MODEL_CONFIGS.m27.toLowerCase() + ) + } // 1P and Foundry: all Claude 4+ models (including Haiku 4.5) if (provider === 'foundry' || provider === 'firstParty') { return !canonical.includes('claude-3-') @@ -116,6 +126,13 @@ export function modelSupportsAdaptiveThinking(model: string): boolean { return supported3P } const canonical = getCanonicalName(model) + const provider = getAPIProvider() + if (provider === 'minimax') { + const normalizedModel = model + .replace(/\[1m\]$/i, '') + .toLowerCase() + return normalizedModel === MINIMAX_MODEL_CONFIGS.m3.toLowerCase() + } // Supported by a subset of Claude 4 models if (canonical.includes('opus-4-6') || canonical.includes('sonnet-4-6')) { return true @@ -139,7 +156,6 @@ export function modelSupportsAdaptiveThinking(model: string): boolean { // Default to true for unknown model strings on 1P and Foundry (because Foundry // is a proxy). Do not default to true for other 3P as they have different formats // for their model strings. - const provider = getAPIProvider() return provider === 'firstParty' || provider === 'foundry' }