-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
feat: add MiniMax provider support #9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,142 @@ | ||
| import { describe, it, expect, beforeEach, afterEach } from 'bun:test' | ||
| import { | ||
| MINIMAX_M2_7_CONFIG, | ||
| MINIMAX_M2_7_HIGHSPEED_CONFIG, | ||
| ALL_MODEL_CONFIGS, | ||
| } from '../configs.js' | ||
| import { | ||
| getAPIProvider, | ||
| isFirstPartyAnthropicBaseUrl, | ||
| } from '../providers.js' | ||
|
|
||
| describe('MiniMax provider', () => { | ||
| let originalEnv: Record<string, string | undefined> | ||
|
|
||
| beforeEach(() => { | ||
| originalEnv = { | ||
| CLAUDE_CODE_USE_BEDROCK: process.env.CLAUDE_CODE_USE_BEDROCK, | ||
| CLAUDE_CODE_USE_VERTEX: process.env.CLAUDE_CODE_USE_VERTEX, | ||
| CLAUDE_CODE_USE_FOUNDRY: process.env.CLAUDE_CODE_USE_FOUNDRY, | ||
| CLAUDE_CODE_USE_MINIMAX: process.env.CLAUDE_CODE_USE_MINIMAX, | ||
| } | ||
| delete process.env.CLAUDE_CODE_USE_BEDROCK | ||
| delete process.env.CLAUDE_CODE_USE_VERTEX | ||
| delete process.env.CLAUDE_CODE_USE_FOUNDRY | ||
| delete process.env.CLAUDE_CODE_USE_MINIMAX | ||
| }) | ||
|
|
||
| afterEach(() => { | ||
| for (const [key, value] of Object.entries(originalEnv)) { | ||
| if (value === undefined) { | ||
| delete process.env[key] | ||
| } else { | ||
| process.env[key] = value | ||
| } | ||
| } | ||
| }) | ||
|
|
||
| describe('getAPIProvider', () => { | ||
| it('returns minimax when CLAUDE_CODE_USE_MINIMAX is set', () => { | ||
| process.env.CLAUDE_CODE_USE_MINIMAX = '1' | ||
| expect(getAPIProvider()).toBe('minimax') | ||
| }) | ||
|
|
||
| it('returns firstParty when CLAUDE_CODE_USE_MINIMAX is not set', () => { | ||
| expect(getAPIProvider()).toBe('firstParty') | ||
| }) | ||
|
|
||
| it('bedrock takes priority over minimax', () => { | ||
| process.env.CLAUDE_CODE_USE_MINIMAX = '1' | ||
| process.env.CLAUDE_CODE_USE_BEDROCK = '1' | ||
| expect(getAPIProvider()).toBe('bedrock') | ||
| }) | ||
|
|
||
| it('vertex takes priority over minimax', () => { | ||
| process.env.CLAUDE_CODE_USE_MINIMAX = '1' | ||
| process.env.CLAUDE_CODE_USE_VERTEX = '1' | ||
| expect(getAPIProvider()).toBe('vertex') | ||
| }) | ||
| }) | ||
|
|
||
| describe('isFirstPartyAnthropicBaseUrl', () => { | ||
| it('returns false when provider is minimax', () => { | ||
| process.env.CLAUDE_CODE_USE_MINIMAX = '1' | ||
| expect(isFirstPartyAnthropicBaseUrl()).toBe(false) | ||
| }) | ||
|
|
||
| it('returns true for firstParty without custom base URL', () => { | ||
| delete process.env.ANTHROPIC_BASE_URL | ||
| expect(isFirstPartyAnthropicBaseUrl()).toBe(true) | ||
| }) | ||
| }) | ||
|
|
||
| describe('MiniMax model configs', () => { | ||
| it('MINIMAX_M2_7_CONFIG has correct model IDs', () => { | ||
| expect(MINIMAX_M2_7_CONFIG.minimax).toBe('MiniMax-M2.7') | ||
| expect(MINIMAX_M2_7_CONFIG.firstParty).toBe('MiniMax-M2.7') | ||
| }) | ||
|
|
||
| it('MINIMAX_M2_7_HIGHSPEED_CONFIG has correct model IDs', () => { | ||
| expect(MINIMAX_M2_7_HIGHSPEED_CONFIG.minimax).toBe('MiniMax-M2.7-highspeed') | ||
| expect(MINIMAX_M2_7_HIGHSPEED_CONFIG.firstParty).toBe('MiniMax-M2.7-highspeed') | ||
| }) | ||
|
|
||
| it('MiniMax configs are registered in ALL_MODEL_CONFIGS', () => { | ||
| expect('minimaxM27' in ALL_MODEL_CONFIGS).toBe(true) | ||
| expect('minimaxM27hs' in ALL_MODEL_CONFIGS).toBe(true) | ||
| }) | ||
|
|
||
| it('minimaxM27 resolves to MiniMax-M2.7 under minimax provider', () => { | ||
| const config = ALL_MODEL_CONFIGS.minimaxM27 | ||
| const resolved = config['minimax'] ?? config.firstParty | ||
| expect(resolved).toBe('MiniMax-M2.7') | ||
| }) | ||
|
|
||
| it('minimaxM27hs resolves to MiniMax-M2.7-highspeed under minimax provider', () => { | ||
| const config = ALL_MODEL_CONFIGS.minimaxM27hs | ||
| const resolved = config['minimax'] ?? config.firstParty | ||
| expect(resolved).toBe('MiniMax-M2.7-highspeed') | ||
| }) | ||
|
|
||
| it('Claude model configs fall back to firstParty under minimax provider', () => { | ||
| const haiku35 = ALL_MODEL_CONFIGS.haiku35 | ||
| // minimax key not present for Claude models — falls back to firstParty | ||
| const resolved = haiku35['minimax'] ?? haiku35.firstParty | ||
| expect(resolved).toBe('claude-3-5-haiku-20241022') | ||
| }) | ||
| }) | ||
|
|
||
| describe('MiniMax API constraints', () => { | ||
| it('default base URL uses overseas api.minimax.io (not api.minimax.chat)', () => { | ||
| const defaultBaseUrl = 'https://api.minimax.io/anthropic' | ||
| expect(defaultBaseUrl).toContain('api.minimax.io') | ||
| expect(defaultBaseUrl).not.toContain('api.minimax.chat') | ||
| }) | ||
|
|
||
| it('filters unsupported parameters for Anthropic-compatible API', () => { | ||
| const UNSUPPORTED_PARAMS = new Set(['top_k', 'stop_sequences', 'service_tier']) | ||
| const input: Record<string, unknown> = { | ||
| model: 'MiniMax-M2.7', | ||
| messages: [{ role: 'user', content: 'hi' }], | ||
| top_k: 40, | ||
| stop_sequences: ['END'], | ||
| temperature: 1.0, | ||
| } | ||
| const filtered = Object.fromEntries( | ||
| Object.entries(input).filter(([k]) => !UNSUPPORTED_PARAMS.has(k)), | ||
| ) | ||
| expect('top_k' in filtered).toBe(false) | ||
| expect('stop_sequences' in filtered).toBe(false) | ||
| expect('temperature' in filtered).toBe(true) | ||
| expect('model' in filtered).toBe(true) | ||
| }) | ||
|
|
||
| it('validates temperature range (0.0, 1.0] — 0 is invalid for MiniMax', () => { | ||
| const isValidTemperature = (t: number) => t > 0 && t <= 1.0 | ||
| expect(isValidTemperature(1.0)).toBe(true) | ||
| expect(isValidTemperature(0.5)).toBe(true) | ||
| expect(isValidTemperature(0.0)).toBe(false) | ||
| expect(isValidTemperature(1.1)).toBe(false) | ||
| }) | ||
| }) | ||
|
Comment on lines
+116
to
+141
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Verify no parameter filtering exists for MiniMax in client.ts
rg -nA20 "CLAUDE_CODE_USE_MINIMAX" src/services/api/client.tsRepository: codeaashu/claude-code Length of output: 1063 🏁 Script executed: #!/bin/bash
# Find where ARGS is defined in client.ts
rg -nB10 "...ARGS" src/services/api/client.ts | head -40Repository: codeaashu/claude-code Length of output: 1637 🏁 Script executed: #!/bin/bash
# Search for any parameter filtering logic in the codebase for MiniMax
rg -n "UNSUPPORTED_PARAMS|top_k|stop_sequences|service_tier" src/services/api/ --type tsRepository: codeaashu/claude-code Length of output: 309 🏁 Script executed: #!/bin/bash
# Check if there's request filtering/interceptor logic
fd . src/services/api -name "*intercept*" -o -name "*filter*" -o -name "*middleware*"Repository: codeaashu/claude-code Length of output: 295 🏁 Script executed: #!/bin/bash
# Look for the complete test file to understand context
wc -l src/utils/model/__tests__/minimax.test.tsRepository: codeaashu/claude-code Length of output: 110 🏁 Script executed: #!/bin/bash
# Get ARGS definition from client.ts
sed -n '141,160p' src/services/api/client.tsRepository: codeaashu/claude-code Length of output: 803 🏁 Script executed: #!/bin/bash
# Read the entire minimax.test.ts to understand test context
cat -n src/utils/model/__tests__/minimax.test.tsRepository: codeaashu/claude-code Length of output: 6239 🏁 Script executed: #!/bin/bash
# Search for any actual usage of parameter filtering or validation for MiniMax
rg -n "top_k|stop_sequences" src/services/api/ --type ts -B2 -A2Repository: codeaashu/claude-code Length of output: 47 🏁 Script executed: #!/bin/bash
# Search for any validation functions or interceptor for MiniMax specifically
rg -n "MiniMax|minimax" src/services/api/client.ts --type ts -B5 -A10 | grep -E "(filter|validate|intercept)" -B5 -A5Repository: codeaashu/claude-code Length of output: 47 Tests document constraints not enforced by the client. The tests at lines 116-132 and 134-140 define parameter filtering and temperature validation as inline utility logic, but these functions are never imported or called in production. They appear to document MiniMax API constraints (unsupported parameters If MiniMax requires this filtering/validation, implement it in the client request handler or request interceptor. Otherwise, clarify the test intent or remove these tests to avoid confusion about what the client actually enforces. 🤖 Prompt for AI Agents |
||
| }) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,7 @@ | ||
| import type { ModelName } from './model.js' | ||
| import type { APIProvider } from './providers.js' | ||
|
|
||
| export type ModelConfig = Record<APIProvider, ModelName> | ||
| export type ModelConfig = Record<'firstParty' | 'bedrock' | 'vertex' | 'foundry', ModelName> & | ||
| Partial<Record<'minimax', ModelName>> | ||
|
Comment on lines
+3
to
+4
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Find direct config indexing with getAPIProvider() that may return undefined for minimax
rg -nP '\b[A-Z_]+_CONFIG\s*\[\s*getAPIProvider\(\)\s*\]' --type=tsRepository: codeaashu/claude-code Length of output: 47 🏁 Script executed: #!/bin/bash
# Check the actual ModelConfig type and config instances in configs.ts
echo "=== Checking src/utils/model/configs.ts ==="
wc -l src/utils/model/configs.tsRepository: codeaashu/claude-code Length of output: 139 🏁 Script executed: #!/bin/bash
# Read the ModelConfig type definition and context around lines 3-4
head -n 20 src/utils/model/configs.tsRepository: codeaashu/claude-code Length of output: 855 🏁 Script executed: #!/bin/bash
# Read the teamMateModel.ts file to check the actual code
cat -n src/utils/swarm/teammateModel.ts | head -n 30Repository: codeaashu/claude-code Length of output: 609 🏁 Script executed: #!/bin/bash
# Search for actual config access patterns without the regex anchors
rg 'CONFIG\[getAPIProvider' --type=ts -A 2 -B 2Repository: codeaashu/claude-code Length of output: 369 🏁 Script executed: #!/bin/bash
# Find all places where getAPIProvider is used with config access
rg 'getAPIProvider\(\)' --type=ts -B 2 -A 2 | head -n 100Repository: codeaashu/claude-code Length of output: 5774 🏁 Script executed: #!/bin/bash
# Find the getAPIProvider function implementation
rg 'export.*getAPIProvider' --type=ts -A 5Repository: codeaashu/claude-code Length of output: 932 🏁 Script executed: #!/bin/bash
# Search for minimax in the configs file to see if any minimax configs were added
rg 'minimax' src/utils/model/configs.ts -i -A 2 -B 2Repository: codeaashu/claude-code Length of output: 1017 🏁 Script executed: #!/bin/bash
# Check what values getAPIProvider() can return
rg "type.*Provider|'minimax'|'firstParty'|'bedrock'" src/utils/model/providers.ts --type=ts | head -n 50Repository: codeaashu/claude-code Length of output: 253 🏁 Script executed: #!/bin/bash
# Look for calls to getHardcodedTeammateModelFallback to see if it's guarded
rg 'getHardcodedTeammateModelFallback' --type=ts -B 3 -A 3Repository: codeaashu/claude-code Length of output: 3484 Runtime error: direct config access returns Making This affects export function getHardcodedTeammateModelFallback(): string {
return CLAUDE_OPUS_4_6_CONFIG[getAPIProvider()] // undefined for minimax
}Add 🤖 Prompt for AI Agents |
||
|
|
||
| // @[MODEL LAUNCH]: Add a new CLAUDE_*_CONFIG constant here. Double check the correct model strings | ||
| // here since the pattern may change. | ||
|
|
@@ -83,6 +83,22 @@ export const CLAUDE_SONNET_4_6_CONFIG = { | |
| foundry: 'claude-sonnet-4-6', | ||
| } as const satisfies ModelConfig | ||
|
|
||
| export const MINIMAX_M2_7_CONFIG = { | ||
| firstParty: 'MiniMax-M2.7', | ||
| bedrock: 'MiniMax-M2.7', | ||
| vertex: 'MiniMax-M2.7', | ||
| foundry: 'MiniMax-M2.7', | ||
| minimax: 'MiniMax-M2.7', | ||
| } as const satisfies ModelConfig | ||
|
|
||
| export const MINIMAX_M2_7_HIGHSPEED_CONFIG = { | ||
| firstParty: 'MiniMax-M2.7-highspeed', | ||
| bedrock: 'MiniMax-M2.7-highspeed', | ||
| vertex: 'MiniMax-M2.7-highspeed', | ||
| foundry: 'MiniMax-M2.7-highspeed', | ||
| minimax: 'MiniMax-M2.7-highspeed', | ||
| } as const satisfies ModelConfig | ||
|
|
||
| // @[MODEL LAUNCH]: Register the new config here. | ||
| export const ALL_MODEL_CONFIGS = { | ||
| haiku35: CLAUDE_3_5_HAIKU_CONFIG, | ||
|
|
@@ -96,6 +112,8 @@ export const ALL_MODEL_CONFIGS = { | |
| opus41: CLAUDE_OPUS_4_1_CONFIG, | ||
| opus45: CLAUDE_OPUS_4_5_CONFIG, | ||
| opus46: CLAUDE_OPUS_4_6_CONFIG, | ||
| minimaxM27: MINIMAX_M2_7_CONFIG, | ||
| minimaxM27hs: MINIMAX_M2_7_HIGHSPEED_CONFIG, | ||
| } as const satisfies Record<string, ModelConfig> | ||
|
|
||
| export type ModelKey = keyof typeof ALL_MODEL_CONFIGS | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,7 @@ | ||
| 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' | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Find all places that check getAPIProvider() for specific provider strings
rg -n "getAPIProvider\(\)" --type=ts -A3 | rg -B1 "(firstParty|bedrock|vertex|foundry)" | head -60Repository: codeaashu/claude-code Length of output: 2237 🏁 Script executed: #!/bin/bash
# Check betas.ts around the structured outputs section (line 140-155)
sed -n '140,160p' src/utils/betas.tsRepository: codeaashu/claude-code Length of output: 965 🏁 Script executed: #!/bin/bash
# Check thinking.ts around the claimed lines (95-110 and 138-144)
sed -n '95,115p' src/utils/thinking.ts
echo "---"
sed -n '135,150p' src/utils/thinking.tsRepository: codeaashu/claude-code Length of output: 1667 🏁 Script executed: #!/bin/bash
# Search WebSearchTool.ts for provider checks
rg -n "getAPIProvider\|provider" src/tools/WebSearchTool/WebSearchTool.ts | head -30Repository: codeaashu/claude-code Length of output: 47 🏁 Script executed: #!/bin/bash
# Read WebSearchTool.ts to find provider checks
wc -l src/tools/WebSearchTool/WebSearchTool.tsRepository: codeaashu/claude-code Length of output: 109 🏁 Script executed: #!/bin/bash
# Search for provider checks in WebSearchTool.ts
grep -n "provider\|getAPIProvider\|minimax\|bedrock\|vertex\|foundry" src/tools/WebSearchTool/WebSearchTool.tsRepository: codeaashu/claude-code Length of output: 286 🏁 Script executed: #!/bin/bash
# Read WebSearchTool.ts around the provider checks
sed -n '160,195p' src/tools/WebSearchTool/WebSearchTool.tsRepository: codeaashu/claude-code Length of output: 975 Feature capability checks don't handle Several functions in the codebase check for specific providers to enable features but don't handle
If MiniMax supports these features via their Anthropic-compatible API, users will experience silent feature degradation. If intentional, add explicit documentation and graceful UI handling for unsupported features. 🤖 Prompt for AI Agents |
||
|
|
||
| export function getAPIProvider(): APIProvider { | ||
| return isEnvTruthy(process.env.CLAUDE_CODE_USE_BEDROCK) | ||
|
|
@@ -10,7 +10,9 @@ export function getAPIProvider(): APIProvider { | |
| ? 'vertex' | ||
| : isEnvTruthy(process.env.CLAUDE_CODE_USE_FOUNDRY) | ||
| ? 'foundry' | ||
| : 'firstParty' | ||
| : isEnvTruthy(process.env.CLAUDE_CODE_USE_MINIMAX) | ||
| ? 'minimax' | ||
| : 'firstParty' | ||
| } | ||
|
|
||
| export function getAPIProviderForStatsig(): AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS { | ||
|
|
@@ -23,6 +25,9 @@ export function getAPIProviderForStatsig(): AnalyticsMetadata_I_VERIFIED_THIS_IS | |
| * (or api-staging.anthropic.com for ant users). | ||
| */ | ||
| export function isFirstPartyAnthropicBaseUrl(): boolean { | ||
| if (getAPIProvider() === 'minimax') { | ||
| return false | ||
| } | ||
| const baseUrl = process.env.ANTHROPIC_BASE_URL | ||
| if (!baseUrl) { | ||
| return true | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing validation for
MINIMAX_API_KEY.When
CLAUDE_CODE_USE_MINIMAXis set butMINIMAX_API_KEYis missing,undefinedis passed to the Anthropic SDK'sapiKeyoption. This will likely cause a cryptic error at request time rather than a clear startup error.Proposed fix
if (isEnvTruthy(process.env.CLAUDE_CODE_USE_MINIMAX)) { + if (!process.env.MINIMAX_API_KEY) { + throw new Error('MINIMAX_API_KEY environment variable is required when using MiniMax provider') + } const minimaxConfig: ConstructorParameters<typeof Anthropic>[0] = { apiKey: process.env.MINIMAX_API_KEY, baseURL: process.env.MINIMAX_BASE_URL ?? 'https://api.minimax.io/anthropic',📝 Committable suggestion
🤖 Prompt for AI Agents