Skip to content

Commit eec7032

Browse files
committed
fix: classify MiniMax profile startup correctly
Recognize MiniMax when /provider loads it through the Anthropic-compatible env shape using ANTHROPIC_BASE_URL, ANTHROPIC_MODEL, and ANTHROPIC_API_KEY. Label MiniMax correctly on the startup screen and skip the Anthropic custom-key approval prompt when the resolved provider is not using the Anthropic account flow. Add regressions for route metadata, legacy provider classification, account-flow bypass, and startup display for Anthropic-compatible MiniMax profiles.
1 parent b5b1ca8 commit eec7032

6 files changed

Lines changed: 56 additions & 3 deletions

File tree

src/components/StartupScreen.test.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ const ENV_KEYS = [
4343
'ANTHROPIC_DEFAULT_SONNET_MODEL',
4444
'ANTHROPIC_DEFAULT_HAIKU_MODEL',
4545
'ANTHROPIC_BASE_URL',
46+
'ANTHROPIC_API_KEY',
4647
]
4748

4849
const originalEnv: Record<string, string | undefined> = {}
@@ -255,6 +256,15 @@ describe('detectProvider — explicit dedicated-provider env flags', () => {
255256
process.env.MINIMAX_API_KEY = 'test-key'
256257
expect(detectProvider().name).toBe('MiniMax')
257258
})
259+
260+
test('Anthropic-compatible MiniMax profile is labeled MiniMax', () => {
261+
process.env.ANTHROPIC_BASE_URL = 'https://api.minimax.io/anthropic'
262+
process.env.ANTHROPIC_API_KEY = 'test-key'
263+
process.env.ANTHROPIC_MODEL = 'MiniMax-M2.7'
264+
265+
expect(detectProvider().name).toBe('MiniMax')
266+
expect(detectProvider().baseUrl).toBe('https://api.minimax.io/anthropic')
267+
})
258268
})
259269

260270
// --- modelOverride from --model flag ---

src/components/StartupScreen.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import { isLocalProviderUrl, resolveProviderRequest } from '../services/api/providerConfig.js'
99
import {
1010
getRouteLabel,
11+
isMiniMaxBaseUrl,
1112
resolveRouteIdFromBaseUrl,
1213
} from '../integrations/routeMetadata.js'
1314
import { getLocalOpenAICompatibleProviderLabel } from '../utils/providerDiscovery.js'
@@ -161,7 +162,8 @@ export function detectProvider(modelOverride?: string): { name: string; model: s
161162
const resolvedModel = parseUserSpecifiedModel(modelSetting)
162163
const baseUrl = process.env.ANTHROPIC_BASE_URL ?? 'https://api.anthropic.com'
163164
const isLocal = isLocalProviderUrl(baseUrl)
164-
return { name: 'Anthropic', model: resolvedModel, baseUrl, isLocal }
165+
const name = isMiniMaxBaseUrl(baseUrl) ? 'MiniMax' : 'Anthropic'
166+
return { name, model: resolvedModel, baseUrl, isLocal }
165167
}
166168

167169
// ─── Box drawing ──────────────────────────────────────────────────────────────

src/integrations/routeMetadata.test.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,16 @@ test('resolveActiveRouteIdFromEnv treats MiniMax credential-only env as MiniMax'
7878
).toBe('minimax')
7979
})
8080

81+
test('resolveActiveRouteIdFromEnv treats Anthropic-compatible MiniMax profile env as MiniMax', () => {
82+
expect(
83+
resolveActiveRouteIdFromEnv({
84+
ANTHROPIC_BASE_URL: 'https://api.minimax.io/anthropic',
85+
ANTHROPIC_API_KEY: 'minimax-key',
86+
ANTHROPIC_MODEL: 'MiniMax-M2.7',
87+
}),
88+
).toBe('minimax')
89+
})
90+
8191
test('resolveActiveRouteIdFromEnv treats Venice credential-only env as Venice', () => {
8292
expect(
8393
resolveActiveRouteIdFromEnv({

src/integrations/routeMetadata.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,11 @@ export function isVeniceBaseUrl(value: string | undefined): boolean {
218218
export function getMiniMaxBaseUrlOverride(
219219
processEnv: NodeJS.ProcessEnv = process.env,
220220
): string | undefined {
221+
const anthropicBaseUrl = processEnv.ANTHROPIC_BASE_URL?.trim()
222+
if (isMiniMaxBaseUrl(anthropicBaseUrl)) {
223+
return anthropicBaseUrl
224+
}
225+
221226
const openAIBaseUrl = processEnv.OPENAI_BASE_URL?.trim()
222227
if (isMiniMaxBaseUrl(openAIBaseUrl)) {
223228
return openAIBaseUrl
@@ -305,8 +310,13 @@ export function hasMiniMaxEnvOnlyProviderIntent(
305310
processEnv: NodeJS.ProcessEnv = process.env,
306311
): boolean {
307312
const hasExplicitMiniMaxIntent = hasMiniMaxRouteIntent(processEnv)
313+
const hasMiniMaxCredential =
314+
hasNonEmptyEnvValue(processEnv.MINIMAX_API_KEY) ||
315+
(isMiniMaxBaseUrl(processEnv.ANTHROPIC_BASE_URL) &&
316+
hasNonEmptyEnvValue(processEnv.ANTHROPIC_API_KEY))
317+
308318
return (
309-
hasNonEmptyEnvValue(processEnv.MINIMAX_API_KEY) &&
319+
hasMiniMaxCredential &&
310320
!hasConflictingOpenAIBaseUrlForRoute(processEnv, isMiniMaxBaseUrl) &&
311321
(hasExplicitMiniMaxIntent ||
312322
(!hasNonEmptyEnvValue(processEnv.OPENAI_API_KEY) &&

src/interactiveHelpers.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ export async function showSetupScreens(root: Root, permissionMode: PermissionMod
213213
// Check for custom API key
214214
// On homespace, ANTHROPIC_API_KEY is preserved in process.env for child
215215
// processes but ignored by Claude Code itself (see auth.ts).
216-
if (process.env.ANTHROPIC_API_KEY && !isRunningOnHomespace()) {
216+
if (usesAnthropicSetup && process.env.ANTHROPIC_API_KEY && !isRunningOnHomespace()) {
217217
const customApiKeyTruncated = normalizeApiKeyForConfig(process.env.ANTHROPIC_API_KEY);
218218
const keyStatus = getCustomApiKeyStatus(customApiKeyTruncated);
219219
if (keyStatus === 'new') {

src/utils/model/providers.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ const originalEnv = {
99
CLAUDE_CODE_USE_FOUNDRY: process.env.CLAUDE_CODE_USE_FOUNDRY,
1010
NVIDIA_NIM: process.env.NVIDIA_NIM,
1111
MINIMAX_API_KEY: process.env.MINIMAX_API_KEY,
12+
ANTHROPIC_BASE_URL: process.env.ANTHROPIC_BASE_URL,
13+
ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY,
14+
ANTHROPIC_MODEL: process.env.ANTHROPIC_MODEL,
1215
OPENAI_BASE_URL: process.env.OPENAI_BASE_URL,
1316
OPENAI_API_BASE: process.env.OPENAI_API_BASE,
1417
OPENAI_MODEL: process.env.OPENAI_MODEL,
@@ -24,6 +27,9 @@ afterEach(() => {
2427
process.env.CLAUDE_CODE_USE_FOUNDRY = originalEnv.CLAUDE_CODE_USE_FOUNDRY
2528
process.env.NVIDIA_NIM = originalEnv.NVIDIA_NIM
2629
process.env.MINIMAX_API_KEY = originalEnv.MINIMAX_API_KEY
30+
process.env.ANTHROPIC_BASE_URL = originalEnv.ANTHROPIC_BASE_URL
31+
process.env.ANTHROPIC_API_KEY = originalEnv.ANTHROPIC_API_KEY
32+
process.env.ANTHROPIC_MODEL = originalEnv.ANTHROPIC_MODEL
2733
process.env.OPENAI_BASE_URL = originalEnv.OPENAI_BASE_URL
2834
process.env.OPENAI_API_BASE = originalEnv.OPENAI_API_BASE
2935
process.env.OPENAI_MODEL = originalEnv.OPENAI_MODEL
@@ -43,6 +49,9 @@ function clearProviderEnv(): void {
4349
delete process.env.CLAUDE_CODE_USE_FOUNDRY
4450
delete process.env.NVIDIA_NIM
4551
delete process.env.MINIMAX_API_KEY
52+
delete process.env.ANTHROPIC_BASE_URL
53+
delete process.env.ANTHROPIC_API_KEY
54+
delete process.env.ANTHROPIC_MODEL
4655
delete process.env.OPENAI_BASE_URL
4756
delete process.env.OPENAI_API_BASE
4857
delete process.env.OPENAI_MODEL
@@ -201,6 +210,18 @@ test('env-only MiniMax API key resolves to the minimax provider', async () => {
201210
expect(getAPIProvider()).toBe('minimax')
202211
})
203212

213+
test('Anthropic-compatible MiniMax profile resolves to the minimax provider', async () => {
214+
clearProviderEnv()
215+
process.env.ANTHROPIC_BASE_URL = 'https://api.minimax.io/anthropic'
216+
process.env.ANTHROPIC_API_KEY = 'minimax-key'
217+
process.env.ANTHROPIC_MODEL = 'MiniMax-M2.7'
218+
219+
const { getAPIProvider, usesAnthropicAccountFlow } =
220+
await importFreshProvidersModule()
221+
expect(getAPIProvider()).toBe('minimax')
222+
expect(usesAnthropicAccountFlow()).toBe(false)
223+
})
224+
204225
test('conflicting OpenAI base prevents env-only MiniMax provider label', async () => {
205226
clearProviderEnv()
206227
process.env.MINIMAX_API_KEY = 'minimax-key'

0 commit comments

Comments
 (0)