| |
| import { getInitialMainLoopModel } from '../../bootstrap/state.js' |
| import { |
| isClaudeAISubscriber, |
| isMaxSubscriber, |
| isTeamPremiumSubscriber, |
| getOpenCodeModelName, |
| } from '../auth.js' |
| import { getModelStrings } from './modelStrings.js' |
| import { |
| COST_TIER_3_15, |
| COST_HAIKU_35, |
| COST_HAIKU_45, |
| formatModelPricing, |
| } from '../modelCost.js' |
| import { getSettings_DEPRECATED } from '../settings/settings.js' |
| import { checkOpus1mAccess, checkSonnet1mAccess } from './check1mAccess.js' |
| import { getAPIProvider } from './providers.js' |
| import { isModelAllowed } from './modelAllowlist.js' |
| import { |
| getCanonicalName, |
| getClaudeAiUserDefaultModelDescription, |
| getDefaultSonnetModel, |
| getDefaultOpusModel, |
| getDefaultHaikuModel, |
| getDefaultMainLoopModelSetting, |
| getMarketingNameForModel, |
| getUserSpecifiedModelSetting, |
| isOpus1mMergeEnabled, |
| getOpus46PricingSuffix, |
| renderDefaultModelSetting, |
| type ModelSetting, |
| } from './model.js' |
| import { has1mContext } from '../context.js' |
| import { getGlobalConfig } from '../config.js' |
|
|
| |
|
|
| export type ModelOption = { |
| value: ModelSetting |
| label: string |
| description: string |
| descriptionForModel?: string |
| } |
|
|
| export function getDefaultOptionForUser(fastMode = false): ModelOption { |
| if (process.env.USER_TYPE === 'ant') { |
| const currentModel = renderDefaultModelSetting( |
| getDefaultMainLoopModelSetting(), |
| ) |
| return { |
| value: null, |
| label: 'Default (recommended)', |
| description: `Use the default model for Ants (currently ${currentModel})`, |
| descriptionForModel: `Default model (currently ${currentModel})`, |
| } |
| } |
|
|
| |
| if (isClaudeAISubscriber()) { |
| return { |
| value: null, |
| label: 'Default (recommended)', |
| description: getClaudeAiUserDefaultModelDescription(fastMode), |
| } |
| } |
|
|
| |
| const provider = getAPIProvider() |
| const isOpenAI = provider === 'openai' |
| const isLocal = provider === 'local' |
| const isOpenCode = provider === 'opencode' |
| const is3P = provider !== 'firstParty' |
| |
| |
| let localModelName: string | null = null |
| if (isLocal || provider === null) { |
| |
| try { |
| |
| const { readFileSync } = require('fs') as typeof import('fs') |
| const { getGlobalClaudeFile } = require('../env.js') as typeof import('../env.js') |
| const raw = readFileSync(getGlobalClaudeFile(), 'utf8') |
| const config = JSON.parse(raw) as { |
| authProvider?: string |
| localBaseUrl?: string |
| localModelName?: string |
| } |
| if (config.authProvider === 'local' && config.localBaseUrl && config.localModelName) { |
| localModelName = config.localModelName |
| } |
| } catch { |
| |
| } |
| } |
| |
| |
| if (isOpenCode) { |
| const currentModel = getOpenCodeModelName() || 'big-pickle' |
| const displayName = currentModel === 'big-pickle' ? 'Big Pickle' : currentModel |
| return { |
| value: null, |
| label: 'Default (recommended)', |
| description: `Use the default OpenCode Zen model (currently ${displayName})`, |
| descriptionForModel: `Default OpenCode Zen model (currently ${displayName})`, |
| } |
| } |
|
|
| |
| if (provider === null) { |
| try { |
| |
| const { readFileSync } = require('fs') as typeof import('fs') |
| const { getGlobalClaudeFile } = require('../env.js') as typeof import('../env.js') |
| const raw = readFileSync(getGlobalClaudeFile(), 'utf8') |
| const fileConfig = JSON.parse(raw) as { |
| authProvider?: string |
| openCodeModelName?: string |
| } |
| if (fileConfig.authProvider === 'opencode') { |
| const currentModel = fileConfig.openCodeModelName || 'big-pickle' |
| const displayName = currentModel === 'big-pickle' ? 'Big Pickle' : currentModel |
| return { |
| value: null, |
| label: 'Default (recommended)', |
| description: `Use the default OpenCode Zen model (currently ${displayName})`, |
| descriptionForModel: `Default OpenCode Zen model (currently ${displayName})`, |
| } |
| } |
| } catch { |
| |
| } |
| } |
| |
| if (localModelName) { |
| return { |
| value: null, |
| label: 'Default (recommended)', |
| description: `Use the default local model (currently ${localModelName})`, |
| descriptionForModel: `Default local model (currently ${localModelName})`, |
| } |
| } |
| |
| return { |
| value: null, |
| label: 'Default (recommended)', |
| description: isOpenAI |
| ? `Use the default OpenAI model (currently ${renderDefaultModelSetting(getDefaultMainLoopModelSetting())})` |
| : `Use the default model (currently ${renderDefaultModelSetting(getDefaultMainLoopModelSetting())})${is3P ? '' : ` · ${formatModelPricing(COST_TIER_3_15)}`}`, |
| } |
| } |
|
|
| function getCustomSonnetOption(): ModelOption | undefined { |
| const is3P = getAPIProvider() !== 'firstParty' |
| const customSonnetModel = process.env.ANTHROPIC_DEFAULT_SONNET_MODEL |
| |
| if (is3P && customSonnetModel) { |
| const is1m = has1mContext(customSonnetModel) |
| return { |
| value: 'sonnet', |
| label: |
| process.env.ANTHROPIC_DEFAULT_SONNET_MODEL_NAME ?? customSonnetModel, |
| description: |
| process.env.ANTHROPIC_DEFAULT_SONNET_MODEL_DESCRIPTION ?? |
| `Custom Sonnet model${is1m ? ' (1M context)' : ''}`, |
| descriptionForModel: `${process.env.ANTHROPIC_DEFAULT_SONNET_MODEL_DESCRIPTION ?? `Custom Sonnet model${is1m ? ' with 1M context' : ''}`} (${customSonnetModel})`, |
| } |
| } |
| } |
|
|
| |
| |
| function getSonnet46Option(): ModelOption { |
| const provider = getAPIProvider() |
| const is3P = provider !== 'firstParty' |
| if (provider === 'openai') { |
| return { |
| value: getModelStrings().sonnet46, |
| label: 'GPT-5.4', |
| description: 'GPT-5.4 · Recommended for most coding tasks', |
| descriptionForModel: |
| 'GPT-5.4 - recommended for most coding and agentic tasks on OpenAI', |
| } |
| } |
| return { |
| value: is3P ? getModelStrings().sonnet46 : 'sonnet', |
| label: 'Sonnet', |
| description: `Sonnet 4.6 · Best for everyday tasks${is3P ? '' : ` · ${formatModelPricing(COST_TIER_3_15)}`}`, |
| descriptionForModel: |
| 'Sonnet 4.6 - best for everyday tasks. Generally recommended for most coding tasks', |
| } |
| } |
|
|
| function getCustomOpusOption(): ModelOption | undefined { |
| const is3P = getAPIProvider() !== 'firstParty' |
| const customOpusModel = process.env.ANTHROPIC_DEFAULT_OPUS_MODEL |
| |
| if (is3P && customOpusModel) { |
| const is1m = has1mContext(customOpusModel) |
| return { |
| value: 'opus', |
| label: process.env.ANTHROPIC_DEFAULT_OPUS_MODEL_NAME ?? customOpusModel, |
| description: |
| process.env.ANTHROPIC_DEFAULT_OPUS_MODEL_DESCRIPTION ?? |
| `Custom Opus model${is1m ? ' (1M context)' : ''}`, |
| descriptionForModel: `${process.env.ANTHROPIC_DEFAULT_OPUS_MODEL_DESCRIPTION ?? `Custom Opus model${is1m ? ' with 1M context' : ''}`} (${customOpusModel})`, |
| } |
| } |
| } |
|
|
| function getOpus41Option(): ModelOption { |
| return { |
| value: 'opus', |
| label: 'Opus 4.1', |
| description: `Opus 4.1 · Legacy`, |
| descriptionForModel: 'Opus 4.1 - legacy version', |
| } |
| } |
|
|
| function getOpus46Option(fastMode = false): ModelOption { |
| const provider = getAPIProvider() |
| const is3P = provider !== 'firstParty' |
| if (provider === 'openai') { |
| return { |
| value: getModelStrings().opus46, |
| label: 'GPT-5.4', |
| description: 'GPT-5.4 · Most capable OpenAI coding model', |
| descriptionForModel: |
| 'GPT-5.4 - most capable OpenAI model for complex coding work', |
| } |
| } |
| return { |
| value: is3P ? getModelStrings().opus46 : 'opus', |
| label: 'Opus', |
| description: `Opus 4.6 · Most capable for complex work${getOpus46PricingSuffix(fastMode)}`, |
| descriptionForModel: 'Opus 4.6 - most capable for complex work', |
| } |
| } |
|
|
| export function getSonnet46_1MOption(): ModelOption { |
| const provider = getAPIProvider() |
| const is3P = provider !== 'firstParty' |
| if (provider === 'openai') { |
| return { |
| value: getModelStrings().sonnet46, |
| label: 'GPT-5.4', |
| description: 'GPT-5.4 for long-running OpenAI sessions', |
| descriptionForModel: |
| 'GPT-5.4 for long-running OpenAI sessions and large codebases', |
| } |
| } |
| return { |
| value: is3P ? getModelStrings().sonnet46 + '[1m]' : 'sonnet[1m]', |
| label: 'Sonnet (1M context)', |
| description: `Sonnet 4.6 for long sessions${is3P ? '' : ` · ${formatModelPricing(COST_TIER_3_15)}`}`, |
| descriptionForModel: |
| 'Sonnet 4.6 with 1M context window - for long sessions with large codebases', |
| } |
| } |
|
|
| export function getOpus46_1MOption(fastMode = false): ModelOption { |
| const provider = getAPIProvider() |
| const is3P = provider !== 'firstParty' |
| if (provider === 'openai') { |
| return { |
| value: getModelStrings().opus46, |
| label: 'GPT-5.4', |
| description: 'GPT-5.4 for long-running OpenAI sessions', |
| descriptionForModel: |
| 'GPT-5.4 for long-running OpenAI sessions and large codebases', |
| } |
| } |
| return { |
| value: is3P ? getModelStrings().opus46 + '[1m]' : 'opus[1m]', |
| label: 'Opus (1M context)', |
| description: `Opus 4.6 for long sessions${getOpus46PricingSuffix(fastMode)}`, |
| descriptionForModel: |
| 'Opus 4.6 with 1M context window - for long sessions with large codebases', |
| } |
| } |
|
|
| function getCustomHaikuOption(): ModelOption | undefined { |
| const is3P = getAPIProvider() !== 'firstParty' |
| const customHaikuModel = process.env.ANTHROPIC_DEFAULT_HAIKU_MODEL |
| |
| if (is3P && customHaikuModel) { |
| return { |
| value: 'haiku', |
| label: process.env.ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME ?? customHaikuModel, |
| description: |
| process.env.ANTHROPIC_DEFAULT_HAIKU_MODEL_DESCRIPTION ?? |
| 'Custom Haiku model', |
| descriptionForModel: `${process.env.ANTHROPIC_DEFAULT_HAIKU_MODEL_DESCRIPTION ?? 'Custom Haiku model'} (${customHaikuModel})`, |
| } |
| } |
| } |
|
|
| function getHaiku45Option(): ModelOption { |
| const provider = getAPIProvider() |
| const is3P = provider !== 'firstParty' |
| if (provider === 'openai') { |
| return { |
| value: getModelStrings().haiku45, |
| label: 'GPT-5.4 Mini', |
| description: 'GPT-5.4 Mini · Fastest OpenAI option for quick answers', |
| descriptionForModel: |
| 'GPT-5.4 Mini - fastest OpenAI option for quick answers and lightweight tasks', |
| } |
| } |
| return { |
| value: 'haiku', |
| label: 'Haiku', |
| description: `Haiku 4.5 · Fastest for quick answers${is3P ? '' : ` · ${formatModelPricing(COST_HAIKU_45)}`}`, |
| descriptionForModel: |
| 'Haiku 4.5 - fastest for quick answers. Lower cost but less capable than Sonnet 4.6.', |
| } |
| } |
|
|
| function getHaiku35Option(): ModelOption { |
| const provider = getAPIProvider() |
| const is3P = provider !== 'firstParty' |
| if (provider === 'openai') { |
| return { |
| value: getModelStrings().haiku35, |
| label: 'GPT-5.4 Mini', |
| description: 'GPT-5.4 Mini · Fastest OpenAI option for simple tasks', |
| descriptionForModel: |
| 'GPT-5.4 Mini - fastest OpenAI option for simple tasks and lightweight requests', |
| } |
| } |
| return { |
| value: 'haiku', |
| label: 'Haiku', |
| description: `Haiku 3.5 for simple tasks${is3P ? '' : ` · ${formatModelPricing(COST_HAIKU_35)}`}`, |
| descriptionForModel: |
| 'Haiku 3.5 - faster and lower cost, but less capable than Sonnet. Use for simple tasks.', |
| } |
| } |
|
|
| function getHaikuOption(): ModelOption { |
| |
| const haikuModel = getDefaultHaikuModel() |
| return haikuModel === getModelStrings().haiku45 |
| ? getHaiku45Option() |
| : getHaiku35Option() |
| } |
|
|
| function getMaxOpusOption(fastMode = false): ModelOption { |
| return { |
| value: 'opus', |
| label: 'Opus', |
| description: `Opus 4.6 · Most capable for complex work${fastMode ? getOpus46PricingSuffix(true) : ''}`, |
| } |
| } |
|
|
| export function getMaxSonnet46_1MOption(): ModelOption { |
| const is3P = getAPIProvider() !== 'firstParty' |
| const billingInfo = isClaudeAISubscriber() ? ' · Billed as extra usage' : '' |
| return { |
| value: 'sonnet[1m]', |
| label: 'Sonnet (1M context)', |
| description: `Sonnet 4.6 with 1M context${billingInfo}${is3P ? '' : ` · ${formatModelPricing(COST_TIER_3_15)}`}`, |
| } |
| } |
|
|
| export function getMaxOpus46_1MOption(fastMode = false): ModelOption { |
| const billingInfo = isClaudeAISubscriber() ? ' · Billed as extra usage' : '' |
| return { |
| value: 'opus[1m]', |
| label: 'Opus (1M context)', |
| description: `Opus 4.6 with 1M context${billingInfo}${getOpus46PricingSuffix(fastMode)}`, |
| } |
| } |
|
|
| function getMergedOpus1MOption(fastMode = false): ModelOption { |
| const is3P = getAPIProvider() !== 'firstParty' |
| return { |
| value: is3P ? getModelStrings().opus46 + '[1m]' : 'opus[1m]', |
| label: 'Opus (1M context)', |
| description: `Opus 4.6 with 1M context · Most capable for complex work${!is3P && fastMode ? getOpus46PricingSuffix(fastMode) : ''}`, |
| descriptionForModel: |
| 'Opus 4.6 with 1M context - most capable for complex work', |
| } |
| } |
|
|
| const MaxSonnet46Option: ModelOption = { |
| value: 'sonnet', |
| label: 'Sonnet', |
| description: 'Sonnet 4.6 · Best for everyday tasks', |
| } |
|
|
| const MaxHaiku45Option: ModelOption = { |
| value: 'haiku', |
| label: 'Haiku', |
| description: 'Haiku 4.5 · Fastest for quick answers', |
| } |
|
|
| function getOpusPlanOption(): ModelOption { |
| return { |
| value: 'opusplan', |
| label: 'Opus Plan Mode', |
| description: 'Use Opus 4.6 in plan mode, Sonnet 4.6 otherwise', |
| } |
| } |
|
|
| |
| |
| function getModelOptionsBase(fastMode = false): ModelOption[] { |
| const provider = getAPIProvider() |
| |
| |
| if (!provider) { |
| return [getDefaultOptionForUser(fastMode)] |
| } |
|
|
| if (process.env.USER_TYPE === 'ant') { |
| |
| const antModelOptions: ModelOption[] = getAntModels().map(m => ({ |
| value: m.alias, |
| label: m.label, |
| description: m.description ?? `[ANT-ONLY] ${m.label} (${m.model})`, |
| })) |
|
|
| return [ |
| getDefaultOptionForUser(), |
| ...antModelOptions, |
| getMergedOpus1MOption(fastMode), |
| getSonnet46Option(), |
| getSonnet46_1MOption(), |
| getHaiku45Option(), |
| ] |
| } |
|
|
| if (isClaudeAISubscriber()) { |
| if (isMaxSubscriber() || isTeamPremiumSubscriber()) { |
| |
| const premiumOptions = [getDefaultOptionForUser(fastMode)] |
| if (!isOpus1mMergeEnabled() && checkOpus1mAccess()) { |
| premiumOptions.push(getMaxOpus46_1MOption(fastMode)) |
| } |
|
|
| premiumOptions.push(MaxSonnet46Option) |
| if (checkSonnet1mAccess()) { |
| premiumOptions.push(getMaxSonnet46_1MOption()) |
| } |
|
|
| premiumOptions.push(MaxHaiku45Option) |
| return premiumOptions |
| } |
|
|
| |
| const standardOptions = [getDefaultOptionForUser(fastMode)] |
| if (checkSonnet1mAccess()) { |
| standardOptions.push(getMaxSonnet46_1MOption()) |
| } |
|
|
| if (isOpus1mMergeEnabled()) { |
| standardOptions.push(getMergedOpus1MOption(fastMode)) |
| } else { |
| standardOptions.push(getMaxOpusOption(fastMode)) |
| if (checkOpus1mAccess()) { |
| standardOptions.push(getMaxOpus46_1MOption(fastMode)) |
| } |
| } |
|
|
| standardOptions.push(MaxHaiku45Option) |
| return standardOptions |
| } |
|
|
| |
| if (getAPIProvider() === 'firstParty') { |
| const payg1POptions = [getDefaultOptionForUser(fastMode)] |
| if (checkSonnet1mAccess()) { |
| payg1POptions.push(getSonnet46_1MOption()) |
| } |
| if (isOpus1mMergeEnabled()) { |
| payg1POptions.push(getMergedOpus1MOption(fastMode)) |
| } else { |
| payg1POptions.push(getOpus46Option(fastMode)) |
| if (checkOpus1mAccess()) { |
| payg1POptions.push(getOpus46_1MOption(fastMode)) |
| } |
| } |
| payg1POptions.push(getHaiku45Option()) |
| return payg1POptions |
| } |
|
|
| |
| if (getAPIProvider() === 'openai') { |
| const openAIOptions = [getDefaultOptionForUser(fastMode)] |
| openAIOptions.push(getSonnet46Option()) |
| openAIOptions.push(getOpus46Option(fastMode)) |
| openAIOptions.push(getHaiku45Option()) |
| return openAIOptions |
| } |
|
|
| |
| if (getAPIProvider() === 'local') { |
| return [getDefaultOptionForUser(fastMode)] |
| } |
|
|
| |
| if (getAPIProvider() === 'nvidia') { |
| |
| |
| void import('../../services/api/nvidiaClient.js').then(({ fetchNvidiaModels }) => { |
| fetchNvidiaModels() |
| }) |
|
|
| const { getCachedNvidiaModels } = require('../../services/api/nvidiaClient.js') as { |
| getCachedNvidiaModels: () => string[] |
| } |
| const models = getCachedNvidiaModels() |
|
|
| if (models && models.length > 0) { |
| return [ |
| getDefaultOptionForUser(fastMode), |
| ...models.map(m => ({ |
| value: m, |
| label: m, |
| description: 'NVIDIA NIM model', |
| })), |
| ] |
| } |
|
|
| return [getDefaultOptionForUser(fastMode)] |
| } |
|
|
| |
| |
| |
| if (getAPIProvider() === 'opencode') { |
| const defaultOpt = getDefaultOptionForUser(fastMode) |
| try { |
| const { getCachedOpencodeModels } = require('../../services/api/opencodeClient.js') |
| const { getOpenCodeApiKey: getApiKey } = require('../../utils/auth.js') |
| const models = getCachedOpencodeModels() |
| const hasApiKey = !!getApiKey() |
| |
| if (models && Array.isArray(models) && models.length > 0) { |
| const filtered = hasApiKey |
| ? models |
| : models.filter(m => m.isFree) |
| |
| if (filtered.length > 0) { |
| return [ |
| defaultOpt, |
| ...filtered.map(m => { |
| let label = m.name || m.id |
| let description = hasApiKey ? 'OpenCode Zen Model' : '限时免费模型' |
| |
| if (m.id === 'big-pickle') { |
| label = 'Big Pickle' |
| description = '旗舰模型,限时免费,适合复杂任务' |
| } else if (m.id === 'gpt-5-nano') { |
| label = 'GPT 5 Nano' |
| description = '永久免费,轻量快速,隐私安全' |
| } else if (m.id === 'minimax-m2.5-free') { |
| label = 'MiniMax M2.5 Free' |
| } else if (m.id === 'hy3-preview-free') { |
| label = 'HY3 Preview Free' |
| } else if (m.id === 'ling-2.6-flash-free') { |
| label = 'Ling 2.6 Flash Free' |
| } else if (m.id === 'trinity-large-preview-free') { |
| label = 'Trinity Large Preview Free' |
| } else if (m.id === 'nemotron-3-super-free') { |
| label = 'Nemotron 3 Super Free' |
| } |
| |
| return { value: m.id, label, description } |
| }), |
| ] |
| } |
| } |
| } catch { |
| |
| } |
| |
| return [defaultOpt] |
| } |
|
|
| |
| if (getAPIProvider() === 'openrouter') { |
| |
| |
| void import('./openRouterModels.js').then(({ startOpenRouterModelsFetch }) => { |
| startOpenRouterModelsFetch() |
| }) |
|
|
| |
| |
| let cachedModels: ModelOption[] = [] |
| try { |
| |
| const openRouterModelsModule = require('./openRouterModels.js') as { |
| getCachedOpenRouterModels: () => ModelOption[] |
| getFirstFreeModel: () => ModelOption | null |
| } |
| cachedModels = openRouterModelsModule.getCachedOpenRouterModels() |
|
|
| |
| const firstFreeModel = openRouterModelsModule.getFirstFreeModel() |
|
|
| |
| if (cachedModels.length > 0) { |
| let modelsToShow = cachedModels |
|
|
| |
| if (firstFreeModel) { |
| |
| modelsToShow = modelsToShow.filter(m => m.value !== firstFreeModel.value) |
| |
| modelsToShow = [firstFreeModel, ...modelsToShow] |
| } |
|
|
| return modelsToShow |
| } |
| } catch { |
| |
| } |
|
|
| |
| |
| return [] |
| } |
|
|
| |
| const payg3pOptions = [getDefaultOptionForUser(fastMode)] |
|
|
| const customSonnet = getCustomSonnetOption() |
| if (customSonnet !== undefined) { |
| payg3pOptions.push(customSonnet) |
| } else { |
| |
| payg3pOptions.push(getSonnet46Option()) |
| if (checkSonnet1mAccess()) { |
| payg3pOptions.push(getSonnet46_1MOption()) |
| } |
| } |
|
|
| const customOpus = getCustomOpusOption() |
| if (customOpus !== undefined) { |
| payg3pOptions.push(customOpus) |
| } else { |
| |
| payg3pOptions.push(getOpus41Option()) |
| payg3pOptions.push(getOpus46Option(fastMode)) |
| if (checkOpus1mAccess()) { |
| payg3pOptions.push(getOpus46_1MOption(fastMode)) |
| } |
| } |
| const customHaiku = getCustomHaikuOption() |
| if (customHaiku !== undefined) { |
| payg3pOptions.push(customHaiku) |
| } else { |
| payg3pOptions.push(getHaikuOption()) |
| } |
| return payg3pOptions |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| function getModelFamilyInfo( |
| model: string, |
| ): { alias: string; currentVersionName: string } | null { |
| const canonical = getCanonicalName(model) |
|
|
| |
| if ( |
| canonical.includes('claude-sonnet-4-6') || |
| canonical.includes('claude-sonnet-4-5') || |
| canonical.includes('claude-sonnet-4-') || |
| canonical.includes('claude-3-7-sonnet') || |
| canonical.includes('claude-3-5-sonnet') |
| ) { |
| const currentName = getMarketingNameForModel(getDefaultSonnetModel()) |
| if (currentName) { |
| return { alias: 'Sonnet', currentVersionName: currentName } |
| } |
| } |
|
|
| |
| if (canonical.includes('claude-opus-4')) { |
| const currentName = getMarketingNameForModel(getDefaultOpusModel()) |
| if (currentName) { |
| return { alias: 'Opus', currentVersionName: currentName } |
| } |
| } |
|
|
| |
| if ( |
| canonical.includes('claude-haiku') || |
| canonical.includes('claude-3-5-haiku') |
| ) { |
| const currentName = getMarketingNameForModel(getDefaultHaikuModel()) |
| if (currentName) { |
| return { alias: 'Haiku', currentVersionName: currentName } |
| } |
| } |
|
|
| return null |
| } |
|
|
| |
| |
| |
| |
| |
| function getKnownModelOption(model: string): ModelOption | null { |
| const marketingName = getMarketingNameForModel(model) |
| if (!marketingName) return null |
|
|
| const familyInfo = getModelFamilyInfo(model) |
| if (!familyInfo) { |
| return { |
| value: model, |
| label: marketingName, |
| description: model, |
| } |
| } |
|
|
| |
| if (marketingName !== familyInfo.currentVersionName) { |
| return { |
| value: model, |
| label: marketingName, |
| description: `Newer version available · select ${familyInfo.alias} for ${familyInfo.currentVersionName}`, |
| } |
| } |
|
|
| |
| return { |
| value: model, |
| label: marketingName, |
| description: model, |
| } |
| } |
|
|
| export function getModelOptions(fastMode = false): ModelOption[] { |
| const options = getModelOptionsBase(fastMode) |
|
|
| |
| const envCustomModel = process.env.ANTHROPIC_CUSTOM_MODEL_OPTION |
| if ( |
| envCustomModel && |
| !options.some(existing => existing.value === envCustomModel) |
| ) { |
| options.push({ |
| value: envCustomModel, |
| label: process.env.ANTHROPIC_CUSTOM_MODEL_OPTION_NAME ?? envCustomModel, |
| description: |
| process.env.ANTHROPIC_CUSTOM_MODEL_OPTION_DESCRIPTION ?? |
| `Custom model (${envCustomModel})`, |
| }) |
| } |
|
|
| |
| for (const opt of getGlobalConfig().additionalModelOptionsCache ?? []) { |
| if (!options.some(existing => existing.value === opt.value)) { |
| options.push(opt) |
| } |
| } |
|
|
| |
| |
| let customModel: ModelSetting = null |
| const currentMainLoopModel = getUserSpecifiedModelSetting() |
| const initialMainLoopModel = getInitialMainLoopModel() |
| if (currentMainLoopModel !== undefined && currentMainLoopModel !== null) { |
| customModel = currentMainLoopModel |
| } else if (initialMainLoopModel !== null) { |
| customModel = initialMainLoopModel |
| } |
| if (customModel === null || options.some(opt => opt.value === customModel)) { |
| return filterModelOptionsByAllowlist(options) |
| } else if (customModel === 'opusplan') { |
| return filterModelOptionsByAllowlist([...options, getOpusPlanOption()]) |
| } else if (customModel === 'opus' && getAPIProvider() === 'firstParty') { |
| return filterModelOptionsByAllowlist([ |
| ...options, |
| getMaxOpusOption(fastMode), |
| ]) |
| } else if (customModel === 'opus[1m]' && getAPIProvider() === 'firstParty') { |
| return filterModelOptionsByAllowlist([ |
| ...options, |
| getMergedOpus1MOption(fastMode), |
| ]) |
| } else { |
| |
| |
| const knownOption = getKnownModelOption(customModel) |
| if (knownOption) { |
| options.push(knownOption) |
| } else { |
| options.push({ |
| value: customModel, |
| label: customModel, |
| description: 'Custom model', |
| }) |
| } |
| return filterModelOptionsByAllowlist(options) |
| } |
| } |
|
|
| |
| |
| |
| |
| function filterModelOptionsByAllowlist(options: ModelOption[]): ModelOption[] { |
| const settings = getSettings_DEPRECATED() || {} |
| if (!settings.availableModels) { |
| return options |
| } |
| return options.filter( |
| opt => |
| opt.value === null || (opt.value !== null && isModelAllowed(opt.value)), |
| ) |
| } |
|
|