Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,30 @@ ANTHROPIC_API_KEY=sk-ant-your-key-here
# GOOGLE_CLOUD_PROJECT=your-gcp-project-id


# -----------------------------------------------------------------------------
# Option 9: NVIDIA NIM
# -----------------------------------------------------------------------------
# NVIDIA NIM provides hosted inference endpoints for NVIDIA models.
# Get your API key from https://build.nvidia.com/
#
# CLAUDE_CODE_USE_OPENAI=1
# NVIDIA_API_KEY=nvapi-your-key-here
# OPENAI_BASE_URL=https://integrate.api.nvidia.com/v1
# OPENAI_MODEL=nvidia/llama-3.1-nemotron-70b-instruct


# -----------------------------------------------------------------------------
# Option 10: MiniMax
# -----------------------------------------------------------------------------
# MiniMax API provides text generation models.
# Get your API key from https://platform.minimax.io/
#
# CLAUDE_CODE_USE_OPENAI=1
# MINIMAX_API_KEY=your-minimax-key-here
# OPENAI_BASE_URL=https://api.minimax.io/v1
# OPENAI_MODEL=MiniMax-M2.5


# =============================================================================
# OPTIONAL TUNING
# =============================================================================
Expand Down
10 changes: 10 additions & 0 deletions src/components/ProviderManager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1009,6 +1009,16 @@ export function ProviderManager({ mode, onDone }: Props): React.ReactNode {
label: 'Custom',
description: 'Any OpenAI-compatible provider',
},
{
value: 'nvidia-nim',
label: 'NVIDIA NIM',
description: 'NVIDIA NIM endpoint',
},
{
value: 'minimax',
label: 'MiniMax',
description: 'MiniMax API endpoint',
},
...(mode === 'first-run'
? [
{
Expand Down
31 changes: 21 additions & 10 deletions src/components/StartupScreen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,17 +117,28 @@ function detectProvider(): { name: string; model: string; baseUrl: string; isLoc
const baseUrl = resolvedRequest.baseUrl
const isLocal = isLocalProviderUrl(baseUrl)
let name = 'OpenAI'
// Override to Codex when resolved endpoint is Codex
if (resolvedRequest.transport === 'codex_responses' || baseUrl.includes('chatgpt.com/backend-api/codex')) {
if (/nvidia/i.test(baseUrl) || /nvidia/i.test(rawModel) || process.env.NVIDIA_NIM)
name = 'NVIDIA NIM'
else if (/minimax/i.test(baseUrl) || /minimax/i.test(rawModel) || process.env.MINIMAX_API_KEY)
name = 'MiniMax'
else if (resolvedRequest.transport === 'codex_responses' || baseUrl.includes('chatgpt.com/backend-api/codex'))
name = 'Codex'
} else if (/deepseek/i.test(baseUrl) || /deepseek/i.test(rawModel)) name = 'DeepSeek'
else if (/openrouter/i.test(baseUrl)) name = 'OpenRouter'
else if (/together/i.test(baseUrl)) name = 'Together AI'
else if (/groq/i.test(baseUrl)) name = 'Groq'
else if (/mistral/i.test(baseUrl) || /mistral/i.test(rawModel)) name = 'Mistral'
else if (/azure/i.test(baseUrl)) name = 'Azure OpenAI'
else if (/llama/i.test(rawModel)) name = 'Meta Llama'
else if (isLocal) name = getLocalOpenAICompatibleProviderLabel(baseUrl)
else if (/deepseek/i.test(baseUrl) || /deepseek/i.test(rawModel))
name = 'DeepSeek'
else if (/openrouter/i.test(baseUrl))
name = 'OpenRouter'
else if (/together/i.test(baseUrl))
name = 'Together AI'
else if (/groq/i.test(baseUrl))
name = 'Groq'
else if (/mistral/i.test(baseUrl) || /mistral/i.test(rawModel))
name = 'Mistral'
else if (/azure/i.test(baseUrl))
name = 'Azure OpenAI'
else if (/llama/i.test(rawModel))
name = 'Meta Llama'
else if (isLocal)
name = getLocalOpenAICompatibleProviderLabel(baseUrl)

// Resolve model alias to actual model name + reasoning effort
let displayModel = resolvedRequest.resolvedModel
Expand Down
8 changes: 6 additions & 2 deletions src/services/api/openaiShim.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1313,8 +1313,12 @@ class OpenAIShimMessages {
...filterAnthropicHeaders(options?.headers),
}

const isGemini = isEnvTruthy(process.env.CLAUDE_CODE_USE_GEMINI)
const apiKey = this.providerOverride?.apiKey ?? process.env.OPENAI_API_KEY ?? ''
const isGemini = isGeminiMode()
const isMiniMax = !!process.env.MINIMAX_API_KEY
const apiKey =
this.providerOverride?.apiKey ??
process.env.OPENAI_API_KEY ??
(isMiniMax ? process.env.MINIMAX_API_KEY : '')
// Detect Azure endpoints by hostname (not raw URL) to prevent bypass via
// path segments like https://evil.com/cognitiveservices.azure.com/
let isAzure = false
Expand Down
24 changes: 23 additions & 1 deletion src/utils/model/configs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ export const CLAUDE_3_7_SONNET_CONFIG = {
gemini: 'gemini-2.0-flash',
github: 'github:copilot',
codex: 'gpt-5.4',
'nvidia-nim': 'nvidia/llama-3.1-nemotron-70b-instruct',
minimax: 'MiniMax-M2.5',
} as const satisfies ModelConfig

export const CLAUDE_3_5_V2_SONNET_CONFIG = {
Expand All @@ -48,6 +50,8 @@ export const CLAUDE_3_5_V2_SONNET_CONFIG = {
gemini: 'gemini-2.0-flash',
github: 'github:copilot',
codex: 'gpt-5.4',
'nvidia-nim': 'nvidia/llama-3.1-nemotron-70b-instruct',
minimax: 'MiniMax-M2.5',
} as const satisfies ModelConfig

export const CLAUDE_3_5_HAIKU_CONFIG = {
Expand All @@ -59,6 +63,8 @@ export const CLAUDE_3_5_HAIKU_CONFIG = {
gemini: 'gemini-2.0-flash-lite',
github: 'github:copilot',
codex: 'gpt-5.4',
'nvidia-nim': 'nvidia/llama-3.1-nemotron-70b-instruct',
minimax: 'MiniMax-M2.5',
} as const satisfies ModelConfig

export const CLAUDE_HAIKU_4_5_CONFIG = {
Expand All @@ -70,6 +76,8 @@ export const CLAUDE_HAIKU_4_5_CONFIG = {
gemini: 'gemini-2.0-flash-lite',
github: 'github:copilot',
codex: 'gpt-5.4',
'nvidia-nim': 'nvidia/llama-3.1-nemotron-70b-instruct',
minimax: 'MiniMax-M2.5',
} as const satisfies ModelConfig

export const CLAUDE_SONNET_4_CONFIG = {
Expand All @@ -81,6 +89,8 @@ export const CLAUDE_SONNET_4_CONFIG = {
gemini: 'gemini-2.0-flash',
github: 'github:copilot',
codex: 'gpt-5.4',
'nvidia-nim': 'nvidia/llama-3.1-nemotron-70b-instruct',
minimax: 'MiniMax-M2.5',
} as const satisfies ModelConfig

export const CLAUDE_SONNET_4_5_CONFIG = {
Expand All @@ -92,6 +102,8 @@ export const CLAUDE_SONNET_4_5_CONFIG = {
gemini: 'gemini-2.0-flash',
github: 'github:copilot',
codex: 'gpt-5.4',
'nvidia-nim': 'nvidia/llama-3.1-nemotron-70b-instruct',
minimax: 'MiniMax-M2.5',
} as const satisfies ModelConfig

export const CLAUDE_OPUS_4_CONFIG = {
Expand All @@ -103,6 +115,8 @@ export const CLAUDE_OPUS_4_CONFIG = {
gemini: 'gemini-2.5-pro-preview-03-25',
github: 'github:copilot',
codex: 'gpt-5.4',
'nvidia-nim': 'nvidia/llama-3.1-nemotron-70b-instruct',
minimax: 'MiniMax-M2.5',
} as const satisfies ModelConfig

export const CLAUDE_OPUS_4_1_CONFIG = {
Expand All @@ -114,6 +128,8 @@ export const CLAUDE_OPUS_4_1_CONFIG = {
gemini: 'gemini-2.5-pro-preview-03-25',
github: 'github:copilot',
codex: 'gpt-5.4',
'nvidia-nim': 'nvidia/llama-3.1-nemotron-70b-instruct',
minimax: 'MiniMax-M2.5',
} as const satisfies ModelConfig

export const CLAUDE_OPUS_4_5_CONFIG = {
Expand All @@ -125,6 +141,8 @@ export const CLAUDE_OPUS_4_5_CONFIG = {
gemini: 'gemini-2.5-pro-preview-03-25',
github: 'github:copilot',
codex: 'gpt-5.4',
'nvidia-nim': 'nvidia/llama-3.1-nemotron-70b-instruct',
minimax: 'MiniMax-M2.5',
} as const satisfies ModelConfig

export const CLAUDE_OPUS_4_6_CONFIG = {
Expand All @@ -136,6 +154,8 @@ export const CLAUDE_OPUS_4_6_CONFIG = {
gemini: 'gemini-2.5-pro-preview-03-25',
github: 'github:copilot',
codex: 'gpt-5.4',
'nvidia-nim': 'nvidia/llama-3.1-nemotron-70b-instruct',
minimax: 'MiniMax-M2.5',
} as const satisfies ModelConfig

export const CLAUDE_SONNET_4_6_CONFIG = {
Expand All @@ -147,6 +167,8 @@ export const CLAUDE_SONNET_4_6_CONFIG = {
gemini: 'gemini-2.0-flash',
github: 'github:copilot',
codex: 'gpt-5.4',
'nvidia-nim': 'nvidia/llama-3.1-nemotron-70b-instruct',
minimax: 'MiniMax-M2.5',
} as const satisfies ModelConfig

// @[MODEL LAUNCH]: Register the new config here.
Expand Down Expand Up @@ -181,4 +203,4 @@ export const CANONICAL_ID_TO_KEY: Record<CanonicalModelId, ModelKey> =
(Object.entries(ALL_MODEL_CONFIGS) as [ModelKey, ModelConfig][]).map(
([key, cfg]) => [cfg.firstParty, key],
),
) as Record<CanonicalModelId, ModelKey>
) as Record<CanonicalModelId, ModelKey>
46 changes: 46 additions & 0 deletions src/utils/model/minimaxModels.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* MiniMax model list for the /model picker.
* Full model catalog from MiniMax API.
*/

import type { ModelOption } from './modelOptions.js'
import { getAPIProvider } from './providers.js'
import { isEnvTruthy } from '../envUtils.js'

export function isMiniMaxProvider(): boolean {
if (isEnvTruthy(process.env.MINIMAX_API_KEY)) {
return true
}
const baseUrl = process.env.OPENAI_BASE_URL ?? ''
if (baseUrl.includes('minimax')) {
return true
}
return getAPIProvider() === 'minimax'
}

function getMiniMaxModels(): ModelOption[] {
return [
// Latest Generation Models - use correct MiniMax naming with M prefix
{ value: 'MiniMax-M2', label: 'MiniMax M2', description: 'MoE model - 131K context - Chat/Code/Reasoning' },
{ value: 'MiniMax-M2.1', label: 'MiniMax M2.1', description: 'Enhanced - 200K context - Vision' },
{ value: 'MiniMax-M2.5', label: 'MiniMax M2.5', description: 'Flagship - 256K context - Vision/Function-calling' },
{ value: 'MiniMax-Text-01', label: 'MiniMax Text 01', description: 'Text-focused - 512K context - FREE' },
{ value: 'MiniMax-Text-01-Preview', label: 'MiniMax Text 01 Preview', description: 'Preview - 256K context - FREE' },
{ value: 'MiniMax-Vision-01', label: 'MiniMax Vision 01', description: 'Vision model - 32K context' },
{ value: 'MiniMax-Vision-01-Fast', label: 'MiniMax Vision 01 Fast', description: 'Fast vision - 16K context - FREE' },
// Legacy free tier models
{ value: 'abab6.5s-chat', label: 'ABAB 6.5S Chat', description: 'Legacy free - 16K context' },
{ value: 'abab6.5-chat', label: 'ABAB 6.5 Chat', description: 'Legacy free - 32K context' },
{ value: 'abab6.5g-chat', label: 'ABAB 6.5G Chat', description: 'Generation 6.5 - 32K context' },
{ value: 'abab6-chat', label: 'ABAB 6 Chat', description: 'Legacy - 8K context' },
]
}

let cachedMiniMaxOptions: ModelOption[] | null = null

export function getCachedMiniMaxModelOptions(): ModelOption[] {
if (!cachedMiniMaxOptions) {
cachedMiniMaxOptions = getMiniMaxModels()
}
return cachedMiniMaxOptions
}
22 changes: 22 additions & 0 deletions src/utils/model/modelOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ import { has1mContext } from '../context.js'
import { getGlobalConfig } from '../config.js'
import { getActiveOpenAIModelOptionsCache } from '../providerProfiles.js'
import { getCachedOllamaModelOptions, isOllamaProvider } from './ollamaModels.js'
import { getCachedNvidiaNimModelOptions, isNvidiaNimProvider } from './nvidiaNimModels.js'
import { getCachedMiniMaxModelOptions, isMiniMaxProvider } from './minimaxModels.js'
import { getAntModels } from './antModels.js'

// @[MODEL LAUNCH]: Update all the available and default model option strings below.
Expand Down Expand Up @@ -390,6 +392,26 @@ function getModelOptionsBase(fastMode = false): ModelOption[] {
return [defaultOption]
}

// When using NVIDIA NIM, show models from the NVIDIA catalog
if (isNvidiaNimProvider()) {
const defaultOption = getDefaultOptionForUser(fastMode)
const nvidiaModels = getCachedNvidiaNimModelOptions()
if (nvidiaModels.length > 0) {
return [defaultOption, ...nvidiaModels]
}
return [defaultOption]
}

// When using MiniMax, show models from the MiniMax catalog
if (isMiniMaxProvider()) {
const defaultOption = getDefaultOptionForUser(fastMode)
const minimaxModels = getCachedMiniMaxModelOptions()
if (minimaxModels.length > 0) {
return [defaultOption, ...minimaxModels]
}
return [defaultOption]
}

if (process.env.USER_TYPE === 'ant') {
// Build options from antModels config
const antModelOptions: ModelOption[] = getAntModels().map(m => ({
Expand Down
Loading
Loading