mirror of
https://github.com/voideditor/void
synced 2026-05-22 08:58:26 +00:00
Refactor model handling and deduplication logic in settings service
- Updated ModelDropdown to use option names directly instead of model names. - Introduced canonicalModelNameForProvider to standardize model names across providers. - Implemented deduplication of provider models to avoid duplicates and manage aliases effectively. - Enhanced model fallback logic in modelCapabilities for better compatibility with new AI models. - Adjusted settings service to incorporate deduplication and normalization of model names.
This commit is contained in:
parent
d4ee802099
commit
d5dcfe2f07
4 changed files with 151 additions and 454 deletions
|
|
@ -37,8 +37,8 @@ const ModelSelectBox = ({ options, featureName, className }: { options: ModelOpt
|
|||
options={options}
|
||||
selectedOption={selectedOption}
|
||||
onChangeOption={onChangeOption}
|
||||
getOptionDisplayName={(option) => option.selection.modelName}
|
||||
getOptionDropdownName={(option) => option.selection.modelName}
|
||||
getOptionDisplayName={(option) => option.name}
|
||||
getOptionDropdownName={(option) => option.name}
|
||||
getOptionDropdownDetail={(option) => option.selection.providerName}
|
||||
getOptionsEqual={(a, b) => optionsEqual([a], [b])}
|
||||
className={className}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
|
||||
*--------------------------------------------------------------------------------------*/
|
||||
|
||||
import { defaultModelsOfProvider } from './modelCapabilities.js';
|
||||
import { ProviderName, VoidStatefulModelInfo } from './voidSettingsTypes.js';
|
||||
|
||||
/** One loaded model per endpoint (mlx_lm.server / afm). */
|
||||
|
|
@ -13,16 +14,100 @@ export type SingleAutodetectedLocalProvider = typeof singleAutodetectedLocalProv
|
|||
const canonicalAppleFoundationModelName = (modelName: string) =>
|
||||
modelName === 'foundation-models' || modelName === 'foundation-model' ? 'foundation' : modelName
|
||||
|
||||
/** Map legacy / alias API ids to one canonical name per provider (avoids duplicate list entries). */
|
||||
export const canonicalModelNameForProvider = (providerName: ProviderName, modelName: string): string => {
|
||||
const lower = modelName.toLowerCase()
|
||||
|
||||
if (providerName === 'appleFoundationModels') {
|
||||
return canonicalAppleFoundationModelName(modelName)
|
||||
}
|
||||
|
||||
if (providerName === 'anthropic') {
|
||||
if (lower === 'claude-sonnet-4-5-20250929') return 'claude-sonnet-4-5'
|
||||
if (lower === 'claude-3-7-sonnet-20250219') return 'claude-3-7-sonnet-latest'
|
||||
if (lower === 'claude-sonnet-4-20250514') return 'claude-sonnet-4-6'
|
||||
if (lower === 'claude-opus-4-20250514') return 'claude-opus-4-6'
|
||||
}
|
||||
|
||||
if (providerName === 'openAI') {
|
||||
if (lower === 'gpt-4o-mini') return 'gpt-4.1-mini'
|
||||
if (lower === 'gpt-4o') return 'gpt-4.1'
|
||||
if (lower === 'o1-mini') return 'o4-mini'
|
||||
if (lower === 'o1') return 'o3'
|
||||
if (lower === 'o3-mini') return 'o4-mini'
|
||||
}
|
||||
|
||||
if (providerName === 'xAI') {
|
||||
if (lower.startsWith('grok-2') || lower.startsWith('grok-3')) return 'grok-4.3'
|
||||
}
|
||||
|
||||
if (providerName === 'mistral') {
|
||||
if (lower === 'magistral-small-latest') return 'mistral-small-latest'
|
||||
if (lower === 'devstral-small-latest') return 'devstral-latest'
|
||||
}
|
||||
|
||||
if (providerName === 'gemini') {
|
||||
if (lower.includes('preview') || lower.includes('-exp-') || lower.includes('2.0') || lower.includes('1.5')) {
|
||||
if (lower.includes('pro')) return 'gemini-2.5-pro'
|
||||
if (lower.includes('flash-lite') || lower.includes('flash_lite')) return 'gemini-2.5-flash-lite'
|
||||
return 'gemini-2.5-flash'
|
||||
}
|
||||
}
|
||||
|
||||
return modelName
|
||||
}
|
||||
|
||||
const modelTypePriority: Record<VoidStatefulModelInfo['type'], number> = {
|
||||
autodetected: 3,
|
||||
default: 2,
|
||||
custom: 1,
|
||||
}
|
||||
|
||||
/** Collapse duplicate model names (aliases, default+custom overlap, etc.). */
|
||||
export const dedupeProviderModels = (providerName: ProviderName, models: VoidStatefulModelInfo[]): VoidStatefulModelInfo[] => {
|
||||
const defaultNames = new Set<string>(defaultModelsOfProvider[providerName] ?? [])
|
||||
const groups = new Map<string, VoidStatefulModelInfo[]>()
|
||||
|
||||
for (const model of models) {
|
||||
const canonical = canonicalModelNameForProvider(providerName, model.modelName)
|
||||
const normalized = canonical === model.modelName ? model : { ...model, modelName: canonical }
|
||||
const key = canonical.toLowerCase()
|
||||
const group = groups.get(key) ?? []
|
||||
group.push(normalized)
|
||||
groups.set(key, group)
|
||||
}
|
||||
|
||||
const deduped: VoidStatefulModelInfo[] = []
|
||||
for (const group of groups.values()) {
|
||||
const best = group.reduce((keep, candidate) => {
|
||||
const keepPriority = modelTypePriority[keep.type]
|
||||
const candidatePriority = modelTypePriority[candidate.type]
|
||||
if (candidatePriority > keepPriority) return candidate
|
||||
if (keepPriority > candidatePriority) return keep
|
||||
const keepInDefaults = defaultNames.has(keep.modelName) ? 1 : 0
|
||||
const candidateInDefaults = defaultNames.has(candidate.modelName) ? 1 : 0
|
||||
return candidateInDefaults > keepInDefaults ? candidate : keep
|
||||
})
|
||||
deduped.push({
|
||||
...best,
|
||||
modelName: best.modelName,
|
||||
isHidden: group.every(m => m.isHidden),
|
||||
})
|
||||
}
|
||||
|
||||
return deduped
|
||||
}
|
||||
|
||||
export const normalizeAutodetectedModelNamesForProvider = (providerName: ProviderName, modelNames: string[]): string[] => {
|
||||
if (providerName === 'appleFoundationModels') {
|
||||
const normalized = modelNames.map(canonicalAppleFoundationModelName)
|
||||
return [new Set(normalized).values().next().value ?? 'foundation']
|
||||
}
|
||||
if (providerName === 'mlx') {
|
||||
const unique = [...new Set(modelNames)]
|
||||
const unique = [...new Set(modelNames.map(n => canonicalModelNameForProvider(providerName, n)))]
|
||||
return unique.length > 0 ? [unique[0]] : []
|
||||
}
|
||||
return modelNames
|
||||
return modelNames.map(n => canonicalModelNameForProvider(providerName, n))
|
||||
}
|
||||
|
||||
export const consolidateSingleAutodetectedProviderModels = (
|
||||
|
|
@ -45,8 +130,8 @@ export const consolidateSingleAutodetectedProviderModels = (
|
|||
modelName: primaryName,
|
||||
type: 'autodetected',
|
||||
}
|
||||
return [
|
||||
return dedupeProviderModels(providerName, [
|
||||
primary,
|
||||
...customModels.filter(m => m.modelName !== primaryName),
|
||||
]
|
||||
...customModels.filter(m => canonicalModelNameForProvider(providerName, m.modelName).toLowerCase() !== primaryName.toLowerCase()),
|
||||
])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -420,8 +420,10 @@ const extensiveModelOptionsFallback: VoidStaticProviderInfo['modelOptionsFallbac
|
|||
if (lower.includes('flash')) return toFallback(geminiModelOptions, 'gemini-2.5-flash')
|
||||
return toFallback(geminiModelOptions, 'gemini-2.5-pro')
|
||||
}
|
||||
if (lower.includes('gemini')) return toFallback(geminiModelOptions, 'gemini-2.5-flash')
|
||||
|
||||
if (lower.includes('claude-3-5') || lower.includes('claude-3.5')) return toFallback(anthropicModelOptions, 'claude-3-5-sonnet-20241022')
|
||||
if (lower.includes('claude-3-7') || lower.includes('claude-3.7')) return toFallback(anthropicModelOptions, 'claude-3-7-sonnet-20250219')
|
||||
if (lower.includes('claude-3-5') || lower.includes('claude-3.5')) return toFallback(anthropicModelOptions, 'claude-sonnet-4-6')
|
||||
if (lower.includes('opus-4-7') || lower.includes('opus-4.7')) return toFallback(anthropicModelOptions, 'claude-opus-4-7')
|
||||
if (lower.includes('sonnet-4-6') || lower.includes('sonnet-4.6')) return toFallback(anthropicModelOptions, 'claude-sonnet-4-6')
|
||||
if (lower.includes('haiku-4-5') || lower.includes('haiku-4.5')) return toFallback(anthropicModelOptions, 'claude-haiku-4-5')
|
||||
|
|
@ -434,7 +436,7 @@ const extensiveModelOptionsFallback: VoidStaticProviderInfo['modelOptionsFallbac
|
|||
if (lower.includes('reasoning')) return toFallback(xAIModelOptions, 'grok-4.20-0309-reasoning')
|
||||
return toFallback(xAIModelOptions, 'grok-4.3')
|
||||
}
|
||||
if (lower.includes('grok2') || lower.includes('grok-2')) return toFallback(xAIModelOptions, 'grok-2')
|
||||
if (lower.includes('grok2') || lower.includes('grok-2') || lower.includes('grok-3') || lower.includes('grok3')) return toFallback(xAIModelOptions, 'grok-4.3')
|
||||
if (lower.includes('grok')) return toFallback(xAIModelOptions, 'grok-4.3')
|
||||
|
||||
if (lower.includes('deepseek') && (lower.includes('v4-pro') || lower.includes('v4_pro'))) return toFallback(deepseekModelOptions, 'deepseek-v4-pro')
|
||||
|
|
@ -473,12 +475,12 @@ const extensiveModelOptionsFallback: VoidStaticProviderInfo['modelOptionsFallbac
|
|||
if (lower.includes('gpt') && lower.includes('nano') && (lower.includes('4.1') || lower.includes('4-1'))) return toFallback(openAIModelOptions, 'gpt-4.1-nano')
|
||||
if (lower.includes('gpt') && (lower.includes('4.1') || lower.includes('4-1'))) return toFallback(openAIModelOptions, 'gpt-4.1')
|
||||
|
||||
if (lower.includes('4o') && lower.includes('mini')) return toFallback(openAIModelOptions, 'gpt-4o-mini')
|
||||
if (lower.includes('4o')) return toFallback(openAIModelOptions, 'gpt-4o')
|
||||
if (lower.includes('4o') && lower.includes('mini')) return toFallback(openAIModelOptions, 'gpt-4.1-mini')
|
||||
if (lower.includes('4o')) return toFallback(openAIModelOptions, 'gpt-4.1')
|
||||
|
||||
if (lower.includes('o1') && lower.includes('mini')) return toFallback(openAIModelOptions, 'o1-mini')
|
||||
if (lower.includes('o1')) return toFallback(openAIModelOptions, 'o1')
|
||||
if (lower.includes('o3') && lower.includes('mini')) return toFallback(openAIModelOptions, 'o3-mini')
|
||||
if (lower.includes('o1') && lower.includes('mini')) return toFallback(openAIModelOptions, 'o4-mini')
|
||||
if (lower.includes('o1')) return toFallback(openAIModelOptions, 'o3')
|
||||
if (lower.includes('o3') && lower.includes('mini')) return toFallback(openAIModelOptions, 'o4-mini')
|
||||
if (lower.includes('o3')) return toFallback(openAIModelOptions, 'o3')
|
||||
if (lower.includes('o4') && lower.includes('mini')) return toFallback(openAIModelOptions, 'o4-mini')
|
||||
|
||||
|
|
@ -565,67 +567,6 @@ const anthropicModelOptions = {
|
|||
reasoningCapabilities: anthropicThinkingCapabilities,
|
||||
|
||||
},
|
||||
'claude-opus-4-20250514': {
|
||||
contextWindow: 200_000,
|
||||
reservedOutputTokenSpace: 8_192,
|
||||
cost: { input: 15.00, cache_read: 1.50, cache_write: 18.75, output: 30.00 },
|
||||
downloadable: false,
|
||||
supportsFIM: false,
|
||||
specialToolFormat: 'anthropic-style',
|
||||
supportsSystemMessage: 'separated',
|
||||
reasoningCapabilities: anthropicThinkingCapabilities,
|
||||
|
||||
},
|
||||
'claude-sonnet-4-20250514': {
|
||||
contextWindow: 200_000,
|
||||
reservedOutputTokenSpace: 8_192,
|
||||
cost: { input: 3.00, cache_read: 0.30, cache_write: 3.75, output: 6.00 },
|
||||
downloadable: false,
|
||||
supportsFIM: false,
|
||||
specialToolFormat: 'anthropic-style',
|
||||
supportsSystemMessage: 'separated',
|
||||
reasoningCapabilities: anthropicThinkingCapabilities,
|
||||
|
||||
},
|
||||
'claude-3-5-sonnet-20241022': {
|
||||
contextWindow: 200_000,
|
||||
reservedOutputTokenSpace: 8_192,
|
||||
cost: { input: 3.00, cache_read: 0.30, cache_write: 3.75, output: 15.00 },
|
||||
downloadable: false,
|
||||
supportsFIM: false,
|
||||
specialToolFormat: 'anthropic-style',
|
||||
supportsSystemMessage: 'separated',
|
||||
reasoningCapabilities: false,
|
||||
},
|
||||
'claude-3-5-haiku-20241022': {
|
||||
contextWindow: 200_000,
|
||||
reservedOutputTokenSpace: 8_192,
|
||||
cost: { input: 0.80, cache_read: 0.08, cache_write: 1.00, output: 4.00 },
|
||||
downloadable: false,
|
||||
supportsFIM: false,
|
||||
specialToolFormat: 'anthropic-style',
|
||||
supportsSystemMessage: 'separated',
|
||||
reasoningCapabilities: false,
|
||||
},
|
||||
'claude-3-opus-20240229': {
|
||||
contextWindow: 200_000,
|
||||
reservedOutputTokenSpace: 4_096,
|
||||
cost: { input: 15.00, cache_read: 1.50, cache_write: 18.75, output: 75.00 },
|
||||
downloadable: false,
|
||||
supportsFIM: false,
|
||||
specialToolFormat: 'anthropic-style',
|
||||
supportsSystemMessage: 'separated',
|
||||
reasoningCapabilities: false,
|
||||
},
|
||||
'claude-3-sonnet-20240229': { // no point of using this, but including this for people who put it in
|
||||
contextWindow: 200_000, cost: { input: 3.00, output: 15.00 },
|
||||
downloadable: false,
|
||||
reservedOutputTokenSpace: 4_096,
|
||||
supportsFIM: false,
|
||||
specialToolFormat: 'anthropic-style',
|
||||
supportsSystemMessage: 'separated',
|
||||
reasoningCapabilities: false,
|
||||
}
|
||||
} as const satisfies { [s: string]: VoidStaticModelInfo }
|
||||
|
||||
const anthropicSettings: VoidStaticProviderInfo = {
|
||||
|
|
@ -650,13 +591,10 @@ const anthropicSettings: VoidStaticProviderInfo = {
|
|||
else if (lower.includes('haiku-4-5') || lower.includes('haiku-4.5')) fallbackName = 'claude-haiku-4-5'
|
||||
else if (lower.includes('opus-4-6') || lower.includes('opus-4.6')) fallbackName = 'claude-opus-4-6'
|
||||
else if (lower.includes('sonnet-4-5') || lower.includes('sonnet-4.5')) fallbackName = 'claude-sonnet-4-5-20250929'
|
||||
else if (lower.includes('claude-4-opus') || lower.includes('claude-opus-4')) fallbackName = 'claude-opus-4-20250514'
|
||||
else if (lower.includes('claude-4-sonnet') || lower.includes('claude-sonnet-4')) fallbackName = 'claude-sonnet-4-20250514'
|
||||
else if (lower.includes('claude-3-7-sonnet')) fallbackName = 'claude-3-7-sonnet-20250219'
|
||||
else if (lower.includes('claude-3-5-sonnet')) fallbackName = 'claude-3-5-sonnet-20241022'
|
||||
else if (lower.includes('claude-3-5-haiku')) fallbackName = 'claude-3-5-haiku-20241022'
|
||||
else if (lower.includes('claude-3-opus')) fallbackName = 'claude-3-opus-20240229'
|
||||
else if (lower.includes('claude-3-sonnet')) fallbackName = 'claude-3-sonnet-20240229'
|
||||
else if (lower.includes('claude-3-5-sonnet') || lower.includes('claude-3-5-haiku') || lower.includes('claude-3-opus') || lower.includes('claude-3-sonnet')) fallbackName = 'claude-sonnet-4-6'
|
||||
else if (lower.includes('claude-4-opus') || lower.includes('claude-opus-4')) fallbackName = 'claude-opus-4-6'
|
||||
else if (lower.includes('claude-4-sonnet') || lower.includes('claude-sonnet-4')) fallbackName = 'claude-sonnet-4-6'
|
||||
if (fallbackName) return { modelName: fallbackName, recognizedModelName: fallbackName, ...anthropicModelOptions[fallbackName] }
|
||||
return null
|
||||
},
|
||||
|
|
@ -762,53 +700,6 @@ const openAIModelOptions = { // https://platform.openai.com/docs/pricing
|
|||
supportsSystemMessage: 'developer-role',
|
||||
reasoningCapabilities: false,
|
||||
},
|
||||
'o1': {
|
||||
contextWindow: 128_000,
|
||||
reservedOutputTokenSpace: 100_000,
|
||||
cost: { input: 15.00, cache_read: 7.50, output: 60.00, },
|
||||
downloadable: false,
|
||||
supportsFIM: false,
|
||||
supportsSystemMessage: 'developer-role',
|
||||
reasoningCapabilities: { supportsReasoning: true, canTurnOffReasoning: false, canIOReasoning: false, reasoningSlider: { type: 'effort_slider', values: ['low', 'medium', 'high'], default: 'low' } },
|
||||
},
|
||||
'o3-mini': {
|
||||
contextWindow: 200_000,
|
||||
reservedOutputTokenSpace: 100_000,
|
||||
cost: { input: 1.10, cache_read: 0.55, output: 4.40, },
|
||||
downloadable: false,
|
||||
supportsFIM: false,
|
||||
supportsSystemMessage: 'developer-role',
|
||||
reasoningCapabilities: { supportsReasoning: true, canTurnOffReasoning: false, canIOReasoning: false, reasoningSlider: { type: 'effort_slider', values: ['low', 'medium', 'high'], default: 'low' } },
|
||||
},
|
||||
'gpt-4o': {
|
||||
contextWindow: 128_000,
|
||||
reservedOutputTokenSpace: 16_384,
|
||||
cost: { input: 2.50, cache_read: 1.25, output: 10.00, },
|
||||
downloadable: false,
|
||||
supportsFIM: false,
|
||||
specialToolFormat: 'openai-style',
|
||||
supportsSystemMessage: 'system-role',
|
||||
reasoningCapabilities: false,
|
||||
},
|
||||
'o1-mini': {
|
||||
contextWindow: 128_000,
|
||||
reservedOutputTokenSpace: 65_536,
|
||||
cost: { input: 1.10, cache_read: 0.55, output: 4.40, },
|
||||
downloadable: false,
|
||||
supportsFIM: false,
|
||||
supportsSystemMessage: false, // does not support any system
|
||||
reasoningCapabilities: { supportsReasoning: true, canTurnOffReasoning: false, canIOReasoning: false, reasoningSlider: { type: 'effort_slider', values: ['low', 'medium', 'high'], default: 'low' } },
|
||||
},
|
||||
'gpt-4o-mini': {
|
||||
contextWindow: 128_000,
|
||||
reservedOutputTokenSpace: 16_384,
|
||||
cost: { input: 0.15, cache_read: 0.075, output: 0.60, },
|
||||
downloadable: false,
|
||||
supportsFIM: false,
|
||||
specialToolFormat: 'openai-style',
|
||||
supportsSystemMessage: 'system-role', // ??
|
||||
reasoningCapabilities: false,
|
||||
},
|
||||
} as const satisfies { [s: string]: VoidStaticModelInfo }
|
||||
|
||||
|
||||
|
|
@ -837,17 +728,17 @@ const openAISettings: VoidStaticProviderInfo = {
|
|||
else fallbackName = 'gpt-5.4'
|
||||
}
|
||||
else if (lower.includes('o4') && lower.includes('mini')) fallbackName = 'o4-mini'
|
||||
else if (lower.includes('o3') && lower.includes('mini')) fallbackName = 'o3-mini'
|
||||
else if (lower.includes('o3') && lower.includes('mini')) fallbackName = 'o4-mini'
|
||||
else if (lower.includes('o3')) fallbackName = 'o3'
|
||||
else if (lower.includes('o1') && lower.includes('mini')) fallbackName = 'o1-mini'
|
||||
else if (lower.includes('o1')) fallbackName = 'o1'
|
||||
else if (lower.includes('o1') && lower.includes('mini')) fallbackName = 'o4-mini'
|
||||
else if (lower.includes('o1')) fallbackName = 'o3'
|
||||
else if (lower.includes('gpt-4.1') || lower.includes('gpt4.1')) {
|
||||
if (lower.includes('nano')) fallbackName = 'gpt-4.1-nano'
|
||||
else if (lower.includes('mini')) fallbackName = 'gpt-4.1-mini'
|
||||
else fallbackName = 'gpt-4.1'
|
||||
}
|
||||
else if (lower.includes('4o') && lower.includes('mini')) fallbackName = 'gpt-4o-mini'
|
||||
else if (lower.includes('4o') || lower.includes('gpt-4o')) fallbackName = 'gpt-4o'
|
||||
else if (lower.includes('4o') && lower.includes('mini')) fallbackName = 'gpt-4.1-mini'
|
||||
else if (lower.includes('4o') || lower.includes('gpt-4o')) fallbackName = 'gpt-4.1'
|
||||
if (fallbackName) return { modelName: fallbackName, recognizedModelName: fallbackName, ...openAIModelOptions[fallbackName] }
|
||||
return null
|
||||
},
|
||||
|
|
@ -897,57 +788,6 @@ const xAIModelOptions = {
|
|||
specialToolFormat: 'openai-style',
|
||||
reasoningCapabilities: false,
|
||||
},
|
||||
'grok-2': {
|
||||
contextWindow: 131_072,
|
||||
reservedOutputTokenSpace: null,
|
||||
cost: { input: 2.00, output: 10.00 },
|
||||
downloadable: false,
|
||||
supportsFIM: false,
|
||||
supportsSystemMessage: 'system-role',
|
||||
specialToolFormat: 'openai-style',
|
||||
reasoningCapabilities: false,
|
||||
},
|
||||
'grok-3': {
|
||||
contextWindow: 131_072,
|
||||
reservedOutputTokenSpace: null,
|
||||
cost: { input: 3.00, output: 15.00 },
|
||||
downloadable: false,
|
||||
supportsFIM: false,
|
||||
supportsSystemMessage: 'system-role',
|
||||
specialToolFormat: 'openai-style',
|
||||
reasoningCapabilities: false,
|
||||
},
|
||||
'grok-3-fast': {
|
||||
contextWindow: 131_072,
|
||||
reservedOutputTokenSpace: null,
|
||||
cost: { input: 5.00, output: 25.00 },
|
||||
downloadable: false,
|
||||
supportsFIM: false,
|
||||
supportsSystemMessage: 'system-role',
|
||||
specialToolFormat: 'openai-style',
|
||||
reasoningCapabilities: false,
|
||||
},
|
||||
// only mini supports thinking
|
||||
'grok-3-mini': {
|
||||
contextWindow: 131_072,
|
||||
reservedOutputTokenSpace: null,
|
||||
cost: { input: 0.30, output: 0.50 },
|
||||
downloadable: false,
|
||||
supportsFIM: false,
|
||||
supportsSystemMessage: 'system-role',
|
||||
specialToolFormat: 'openai-style',
|
||||
reasoningCapabilities: { supportsReasoning: true, canTurnOffReasoning: false, canIOReasoning: false, reasoningSlider: { type: 'effort_slider', values: ['low', 'high'], default: 'low' } },
|
||||
},
|
||||
'grok-3-mini-fast': {
|
||||
contextWindow: 131_072,
|
||||
reservedOutputTokenSpace: null,
|
||||
cost: { input: 0.60, output: 4.00 },
|
||||
downloadable: false,
|
||||
supportsFIM: false,
|
||||
supportsSystemMessage: 'system-role',
|
||||
specialToolFormat: 'openai-style',
|
||||
reasoningCapabilities: { supportsReasoning: true, canTurnOffReasoning: false, canIOReasoning: false, reasoningSlider: { type: 'effort_slider', values: ['low', 'high'], default: 'low' } },
|
||||
},
|
||||
} as const satisfies { [s: string]: VoidStaticModelInfo }
|
||||
|
||||
const xAISettings: VoidStaticProviderInfo = {
|
||||
|
|
@ -960,8 +800,7 @@ const xAISettings: VoidStaticProviderInfo = {
|
|||
else fallbackName = 'grok-4.20-0309-reasoning'
|
||||
}
|
||||
else if (lower.includes('grok-4') || lower.includes('grok4.3') || lower.includes('grok-4.3')) fallbackName = 'grok-4.3'
|
||||
else if (lower.includes('grok-2')) fallbackName = 'grok-2'
|
||||
else if (lower.includes('grok-3')) fallbackName = 'grok-4.3' // grok-3 aliases redirect to grok-4.3
|
||||
else if (lower.includes('grok-2') || lower.includes('grok-3')) fallbackName = 'grok-4.3'
|
||||
else if (lower.includes('grok')) fallbackName = 'grok-4.3'
|
||||
if (fallbackName) return { modelName: fallbackName, recognizedModelName: fallbackName, ...xAIModelOptions[fallbackName] }
|
||||
return null
|
||||
|
|
@ -1039,115 +878,6 @@ const geminiModelOptions = { // https://ai.google.dev/gemini-api/docs/pricing
|
|||
reasoningReservedOutputTokenSpace: 8192,
|
||||
},
|
||||
},
|
||||
// https://ai.google.dev/gemini-api/docs/thinking#set-budget
|
||||
'gemini-2.5-pro-preview-05-06': {
|
||||
contextWindow: 1_048_576,
|
||||
reservedOutputTokenSpace: 8_192,
|
||||
cost: { input: 0, output: 0 },
|
||||
downloadable: false,
|
||||
supportsFIM: false,
|
||||
supportsSystemMessage: 'separated',
|
||||
specialToolFormat: 'gemini-style',
|
||||
reasoningCapabilities: {
|
||||
supportsReasoning: true,
|
||||
canTurnOffReasoning: true,
|
||||
canIOReasoning: false,
|
||||
reasoningSlider: { type: 'budget_slider', min: 1024, max: 8192, default: 1024 }, // max is really 24576
|
||||
reasoningReservedOutputTokenSpace: 8192,
|
||||
},
|
||||
},
|
||||
'gemini-2.0-flash-lite': {
|
||||
contextWindow: 1_048_576,
|
||||
reservedOutputTokenSpace: 8_192,
|
||||
cost: { input: 0, output: 0 },
|
||||
downloadable: false,
|
||||
supportsFIM: false,
|
||||
supportsSystemMessage: 'separated',
|
||||
specialToolFormat: 'gemini-style',
|
||||
reasoningCapabilities: false, // no reasoning
|
||||
},
|
||||
'gemini-2.5-flash-preview-04-17': {
|
||||
contextWindow: 1_048_576,
|
||||
reservedOutputTokenSpace: 8_192,
|
||||
cost: { input: 0.15, output: .60 }, // TODO $3.50 output with thinking not included
|
||||
downloadable: false,
|
||||
supportsFIM: false,
|
||||
supportsSystemMessage: 'separated',
|
||||
specialToolFormat: 'gemini-style',
|
||||
reasoningCapabilities: {
|
||||
supportsReasoning: true,
|
||||
canTurnOffReasoning: true,
|
||||
canIOReasoning: false,
|
||||
reasoningSlider: { type: 'budget_slider', min: 1024, max: 8192, default: 1024 }, // max is really 24576
|
||||
reasoningReservedOutputTokenSpace: 8192,
|
||||
},
|
||||
},
|
||||
'gemini-2.5-pro-exp-03-25': {
|
||||
contextWindow: 1_048_576,
|
||||
reservedOutputTokenSpace: 8_192,
|
||||
cost: { input: 0, output: 0 },
|
||||
downloadable: false,
|
||||
supportsFIM: false,
|
||||
supportsSystemMessage: 'separated',
|
||||
specialToolFormat: 'gemini-style',
|
||||
reasoningCapabilities: {
|
||||
supportsReasoning: true,
|
||||
canTurnOffReasoning: true,
|
||||
canIOReasoning: false,
|
||||
reasoningSlider: { type: 'budget_slider', min: 1024, max: 8192, default: 1024 }, // max is really 24576
|
||||
reasoningReservedOutputTokenSpace: 8192,
|
||||
},
|
||||
},
|
||||
'gemini-2.0-flash': {
|
||||
contextWindow: 1_048_576,
|
||||
reservedOutputTokenSpace: 8_192, // 8_192,
|
||||
cost: { input: 0.10, output: 0.40 },
|
||||
downloadable: false,
|
||||
supportsFIM: false,
|
||||
supportsSystemMessage: 'separated',
|
||||
specialToolFormat: 'gemini-style',
|
||||
reasoningCapabilities: false,
|
||||
},
|
||||
'gemini-2.0-flash-lite-preview-02-05': {
|
||||
contextWindow: 1_048_576,
|
||||
reservedOutputTokenSpace: 8_192, // 8_192,
|
||||
cost: { input: 0.075, output: 0.30 },
|
||||
downloadable: false,
|
||||
supportsFIM: false,
|
||||
supportsSystemMessage: 'separated',
|
||||
specialToolFormat: 'gemini-style',
|
||||
reasoningCapabilities: false,
|
||||
},
|
||||
'gemini-1.5-flash': {
|
||||
contextWindow: 1_048_576,
|
||||
reservedOutputTokenSpace: 8_192, // 8_192,
|
||||
cost: { input: 0.075, output: 0.30 }, // TODO!!! price doubles after 128K tokens, we are NOT encoding that info right now
|
||||
downloadable: false,
|
||||
supportsFIM: false,
|
||||
supportsSystemMessage: 'separated',
|
||||
specialToolFormat: 'gemini-style',
|
||||
reasoningCapabilities: false,
|
||||
},
|
||||
'gemini-1.5-pro': {
|
||||
contextWindow: 2_097_152,
|
||||
reservedOutputTokenSpace: 8_192,
|
||||
cost: { input: 1.25, output: 5.00 }, // TODO!!! price doubles after 128K tokens, we are NOT encoding that info right now
|
||||
downloadable: false,
|
||||
supportsFIM: false,
|
||||
supportsSystemMessage: 'separated',
|
||||
specialToolFormat: 'gemini-style',
|
||||
reasoningCapabilities: false,
|
||||
},
|
||||
'gemini-1.5-flash-8b': {
|
||||
contextWindow: 1_048_576,
|
||||
reservedOutputTokenSpace: 8_192,
|
||||
cost: { input: 0.0375, output: 0.15 }, // TODO!!! price doubles after 128K tokens, we are NOT encoding that info right now
|
||||
downloadable: false,
|
||||
supportsFIM: false,
|
||||
supportsSystemMessage: 'separated',
|
||||
specialToolFormat: 'gemini-style',
|
||||
reasoningCapabilities: false,
|
||||
},
|
||||
} as const satisfies { [s: string]: VoidStaticModelInfo }
|
||||
|
||||
const geminiSettings: VoidStaticProviderInfo = {
|
||||
|
|
@ -1161,9 +891,7 @@ const geminiSettings: VoidStaticProviderInfo = {
|
|||
else if (lower.includes('flash')) fallbackName = 'gemini-2.5-flash'
|
||||
else fallbackName = 'gemini-2.5-pro'
|
||||
}
|
||||
else if (lower.includes('2.0') && lower.includes('flash')) fallbackName = 'gemini-2.0-flash'
|
||||
else if (lower.includes('1.5') && lower.includes('pro')) fallbackName = 'gemini-1.5-pro'
|
||||
else if (lower.includes('1.5') && lower.includes('flash')) fallbackName = 'gemini-1.5-flash'
|
||||
else if (lower.includes('2.0') || lower.includes('1.5') || lower.includes('preview') || lower.includes('-exp-')) fallbackName = 'gemini-2.5-flash'
|
||||
if (fallbackName) return { modelName: fallbackName, recognizedModelName: fallbackName, ...geminiModelOptions[fallbackName] }
|
||||
return null
|
||||
},
|
||||
|
|
@ -1289,26 +1017,6 @@ const mistralModelOptions = { // https://docs.mistral.ai/getting-started/models/
|
|||
supportsSystemMessage: 'system-role',
|
||||
reasoningCapabilities: { supportsReasoning: true, canIOReasoning: true, canTurnOffReasoning: false, openSourceThinkTags: ['<think>', '</think>'] },
|
||||
},
|
||||
'magistral-small-latest': { // Magistral Small 1.2 (deprecated → Mistral Small 4) — https://docs.mistral.ai/models/model-cards/magistral-small-1-2-25-09
|
||||
contextWindow: 128_000,
|
||||
reservedOutputTokenSpace: 8_192,
|
||||
cost: { input: 0.30, output: 0.90 },
|
||||
supportsFIM: false,
|
||||
specialToolFormat: 'openai-style',
|
||||
downloadable: { sizeGb: 'not-known' },
|
||||
supportsSystemMessage: 'system-role',
|
||||
reasoningCapabilities: { supportsReasoning: true, canIOReasoning: true, canTurnOffReasoning: false, openSourceThinkTags: ['<think>', '</think>'] },
|
||||
},
|
||||
'devstral-small-latest': { // Devstral Small 2 (labs, deprecated → Devstral 2) — https://docs.mistral.ai/models/model-cards/devstral-small-2-25-12
|
||||
contextWindow: 256_000,
|
||||
reservedOutputTokenSpace: 8_192,
|
||||
cost: { input: 0.20, output: 0.80 },
|
||||
supportsFIM: false,
|
||||
specialToolFormat: 'openai-style',
|
||||
downloadable: { sizeGb: 14 },
|
||||
supportsSystemMessage: 'system-role',
|
||||
reasoningCapabilities: false,
|
||||
},
|
||||
'ministral-14b-latest': { // Ministral 3 14B — https://docs.mistral.ai/models/model-cards/ministral-3-14b-25-12
|
||||
contextWindow: 256_000,
|
||||
reservedOutputTokenSpace: 4_096,
|
||||
|
|
@ -1347,8 +1055,8 @@ const mistralSettings: VoidStaticProviderInfo = {
|
|||
const lower = modelName.toLowerCase()
|
||||
let fallbackName: keyof typeof mistralModelOptions | null = null
|
||||
if (lower.includes('codestral')) fallbackName = 'codestral-latest'
|
||||
else if (lower.includes('magistral')) fallbackName = lower.includes('small') ? 'magistral-small-latest' : 'magistral-medium-latest'
|
||||
else if (lower.includes('devstral')) fallbackName = lower.includes('small') ? 'devstral-small-latest' : 'devstral-latest'
|
||||
else if (lower.includes('magistral')) fallbackName = lower.includes('small') ? 'mistral-small-latest' : 'magistral-medium-latest'
|
||||
else if (lower.includes('devstral')) fallbackName = 'devstral-latest'
|
||||
else if (lower.includes('ministral')) {
|
||||
if (lower.includes('14')) fallbackName = 'ministral-14b-latest'
|
||||
else if (lower.includes('8')) fallbackName = 'ministral-8b-latest'
|
||||
|
|
@ -1719,51 +1427,6 @@ const openRouterModelOptions_assumingOpenAICompat = {
|
|||
supportsSystemMessage: 'system-role',
|
||||
reasoningCapabilities: { supportsReasoning: true, canIOReasoning: true, canTurnOffReasoning: false },
|
||||
},
|
||||
'microsoft/phi-4-reasoning-plus:free': { // a 14B model...
|
||||
contextWindow: 32_768,
|
||||
reservedOutputTokenSpace: null,
|
||||
cost: { input: 0, output: 0 },
|
||||
downloadable: false,
|
||||
supportsFIM: false,
|
||||
supportsSystemMessage: 'system-role',
|
||||
reasoningCapabilities: { supportsReasoning: true, canIOReasoning: true, canTurnOffReasoning: false },
|
||||
},
|
||||
'mistralai/mistral-small-3.1-24b-instruct:free': {
|
||||
contextWindow: 128_000,
|
||||
reservedOutputTokenSpace: null,
|
||||
cost: { input: 0, output: 0 },
|
||||
downloadable: false,
|
||||
supportsFIM: false,
|
||||
supportsSystemMessage: 'system-role',
|
||||
reasoningCapabilities: false,
|
||||
},
|
||||
'google/gemini-2.0-flash-lite-preview-02-05:free': {
|
||||
contextWindow: 1_048_576,
|
||||
reservedOutputTokenSpace: null,
|
||||
cost: { input: 0, output: 0 },
|
||||
downloadable: false,
|
||||
supportsFIM: false,
|
||||
supportsSystemMessage: 'system-role',
|
||||
reasoningCapabilities: false,
|
||||
},
|
||||
'google/gemini-2.0-pro-exp-02-05:free': {
|
||||
contextWindow: 1_048_576,
|
||||
reservedOutputTokenSpace: null,
|
||||
cost: { input: 0, output: 0 },
|
||||
downloadable: false,
|
||||
supportsFIM: false,
|
||||
supportsSystemMessage: 'system-role',
|
||||
reasoningCapabilities: false,
|
||||
},
|
||||
'google/gemini-2.0-flash-exp:free': {
|
||||
contextWindow: 1_048_576,
|
||||
reservedOutputTokenSpace: null,
|
||||
cost: { input: 0, output: 0 },
|
||||
downloadable: false,
|
||||
supportsFIM: false,
|
||||
supportsSystemMessage: 'system-role',
|
||||
reasoningCapabilities: false,
|
||||
},
|
||||
'deepseek/deepseek-r1': {
|
||||
...openSourceModelOptions_assumingOAICompat.deepseekR1,
|
||||
contextWindow: 128_000,
|
||||
|
|
@ -1771,87 +1434,6 @@ const openRouterModelOptions_assumingOpenAICompat = {
|
|||
cost: { input: 0.8, output: 2.4 },
|
||||
downloadable: false,
|
||||
},
|
||||
'anthropic/claude-opus-4': {
|
||||
contextWindow: 200_000,
|
||||
reservedOutputTokenSpace: null,
|
||||
cost: { input: 15.00, output: 75.00 },
|
||||
downloadable: false,
|
||||
supportsFIM: false,
|
||||
supportsSystemMessage: 'system-role',
|
||||
reasoningCapabilities: false,
|
||||
},
|
||||
'anthropic/claude-sonnet-4': {
|
||||
contextWindow: 200_000,
|
||||
reservedOutputTokenSpace: null,
|
||||
cost: { input: 3.00, output: 15.00 },
|
||||
downloadable: false,
|
||||
supportsFIM: false,
|
||||
supportsSystemMessage: 'system-role',
|
||||
reasoningCapabilities: false,
|
||||
},
|
||||
'anthropic/claude-3.7-sonnet:thinking': {
|
||||
contextWindow: 200_000,
|
||||
reservedOutputTokenSpace: null,
|
||||
cost: { input: 3.00, output: 15.00 },
|
||||
downloadable: false,
|
||||
supportsFIM: false,
|
||||
supportsSystemMessage: 'system-role',
|
||||
reasoningCapabilities: { // same as anthropic, see above
|
||||
supportsReasoning: true,
|
||||
canTurnOffReasoning: false,
|
||||
canIOReasoning: true,
|
||||
reasoningReservedOutputTokenSpace: 8192,
|
||||
reasoningSlider: { type: 'budget_slider', min: 1024, max: 8192, default: 1024 }, // they recommend batching if max > 32_000.
|
||||
},
|
||||
},
|
||||
'anthropic/claude-3.7-sonnet': {
|
||||
contextWindow: 200_000,
|
||||
reservedOutputTokenSpace: null,
|
||||
cost: { input: 3.00, output: 15.00 },
|
||||
downloadable: false,
|
||||
supportsFIM: false,
|
||||
supportsSystemMessage: 'system-role',
|
||||
reasoningCapabilities: false, // stupidly, openrouter separates thinking from non-thinking
|
||||
},
|
||||
'anthropic/claude-3.5-sonnet': {
|
||||
contextWindow: 200_000,
|
||||
reservedOutputTokenSpace: null,
|
||||
cost: { input: 3.00, output: 15.00 },
|
||||
downloadable: false,
|
||||
supportsFIM: false,
|
||||
supportsSystemMessage: 'system-role',
|
||||
reasoningCapabilities: false,
|
||||
},
|
||||
'mistralai/codestral-2501': {
|
||||
...openSourceModelOptions_assumingOAICompat.codestral,
|
||||
contextWindow: 256_000,
|
||||
reservedOutputTokenSpace: null,
|
||||
cost: { input: 0.3, output: 0.9 },
|
||||
downloadable: false,
|
||||
reasoningCapabilities: false,
|
||||
},
|
||||
'mistralai/devstral-small:free': {
|
||||
...openSourceModelOptions_assumingOAICompat.devstral,
|
||||
contextWindow: 130_000,
|
||||
reservedOutputTokenSpace: null,
|
||||
cost: { input: 0, output: 0 },
|
||||
downloadable: false,
|
||||
reasoningCapabilities: false,
|
||||
},
|
||||
'qwen/qwen-2.5-coder-32b-instruct': {
|
||||
...openSourceModelOptions_assumingOAICompat['qwen2.5coder'],
|
||||
contextWindow: 33_000,
|
||||
reservedOutputTokenSpace: null,
|
||||
cost: { input: 0.07, output: 0.16 },
|
||||
downloadable: false,
|
||||
},
|
||||
'qwen/qwq-32b': {
|
||||
...openSourceModelOptions_assumingOAICompat['qwq'],
|
||||
contextWindow: 33_000,
|
||||
reservedOutputTokenSpace: null,
|
||||
cost: { input: 0.07, output: 0.16 },
|
||||
downloadable: false,
|
||||
}
|
||||
} as const satisfies { [s: string]: VoidStaticModelInfo }
|
||||
|
||||
const openRouterSettings: VoidStaticProviderInfo = {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import { IMetricsService } from './metricsService.js';
|
|||
import { defaultProviderSettings, getModelCapabilities, ModelOverrides } from './modelCapabilities.js';
|
||||
import { VOID_SETTINGS_STORAGE_KEY } from './storageKeys.js';
|
||||
import { isMacintosh } from '../../../../base/common/platform.js';
|
||||
import { consolidateSingleAutodetectedProviderModels, normalizeAutodetectedModelNamesForProvider } from './localSingleModelProviders.js';
|
||||
import { consolidateSingleAutodetectedProviderModels, dedupeProviderModels, normalizeAutodetectedModelNamesForProvider } from './localSingleModelProviders.js';
|
||||
import { defaultSettingsOfProvider, FeatureName, ProviderName, ModelSelectionOfFeature, SettingsOfProvider, SettingName, providerNames, ModelSelection, modelSelectionsEqual, featureNames, VoidStatefulModelInfo, GlobalSettings, GlobalSettingName, defaultGlobalSettings, ModelSelectionOptions, OptionsOfModelSelection, ChatMode, OverridesOfModel, defaultOverridesOfModel, MCPUserStateOfName as MCPUserStateOfName, MCPUserState } from './voidSettingsTypes.js';
|
||||
|
||||
|
||||
|
|
@ -86,8 +86,8 @@ export interface IVoidSettingsService {
|
|||
|
||||
|
||||
|
||||
const _modelsWithSwappedInNewModels = (options: { existingModels: VoidStatefulModelInfo[], models: string[], type: 'autodetected' | 'default' }) => {
|
||||
const { existingModels, models, type } = options
|
||||
const _modelsWithSwappedInNewModels = (options: { providerName: ProviderName, existingModels: VoidStatefulModelInfo[], models: string[], type: 'autodetected' | 'default' }) => {
|
||||
const { providerName, existingModels, models, type } = options
|
||||
|
||||
const existingModelsMap: Record<string, VoidStatefulModelInfo> = {}
|
||||
for (const existingModel of existingModels) {
|
||||
|
|
@ -96,13 +96,13 @@ const _modelsWithSwappedInNewModels = (options: { existingModels: VoidStatefulMo
|
|||
|
||||
const newDefaultModels = models.map((modelName, i) => ({ modelName, type, isHidden: !!existingModelsMap[modelName]?.isHidden, }))
|
||||
|
||||
return [
|
||||
return dedupeProviderModels(providerName, [
|
||||
...newDefaultModels, // swap out all the models of this type for the new models of this type
|
||||
...existingModels.filter(m => {
|
||||
const keep = m.type !== type
|
||||
return keep
|
||||
})
|
||||
]
|
||||
])
|
||||
}
|
||||
|
||||
export const modelFilterOfFeatureName: {
|
||||
|
|
@ -129,7 +129,7 @@ const _stateWithMergedDefaultModels = (state: VoidSettingsState): VoidSettingsSt
|
|||
const defaultModels = defaultSettingsOfProvider[providerName]?.models ?? []
|
||||
const currentModels = newSettingsOfProvider[providerName]?.models ?? []
|
||||
const defaultModelNames = defaultModels.map(m => m.modelName)
|
||||
const newModels = _modelsWithSwappedInNewModels({ existingModels: currentModels, models: defaultModelNames, type: 'default' })
|
||||
const newModels = _modelsWithSwappedInNewModels({ providerName, existingModels: currentModels, models: defaultModelNames, type: 'default' })
|
||||
newSettingsOfProvider = {
|
||||
...newSettingsOfProvider,
|
||||
[providerName]: {
|
||||
|
|
@ -165,14 +165,40 @@ const _validatedModelState = (state: Omit<VoidSettingsState, '_modelOptions'>):
|
|||
}
|
||||
}
|
||||
|
||||
// dedupe models (aliases, default+custom overlap, persisted legacy ids)
|
||||
for (const providerName of providerNames) {
|
||||
const models = newSettingsOfProvider[providerName].models
|
||||
const deduped = dedupeProviderModels(providerName, models)
|
||||
if (deduped.length !== models.length || deduped.some((m, i) => m.modelName !== models[i]?.modelName || m.type !== models[i]?.type || m.isHidden !== models[i]?.isHidden)) {
|
||||
newSettingsOfProvider = {
|
||||
...newSettingsOfProvider,
|
||||
[providerName]: {
|
||||
...newSettingsOfProvider[providerName],
|
||||
models: deduped,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update model options
|
||||
const visibleModelNameCounts = new Map<string, number>()
|
||||
for (const providerName of providerNames) {
|
||||
if (!newSettingsOfProvider[providerName]._didFillInProviderSettings) continue
|
||||
for (const { modelName, isHidden } of newSettingsOfProvider[providerName].models) {
|
||||
if (isHidden) continue
|
||||
visibleModelNameCounts.set(modelName, (visibleModelNameCounts.get(modelName) ?? 0) + 1)
|
||||
}
|
||||
}
|
||||
|
||||
let newModelOptions: ModelOption[] = []
|
||||
for (const providerName of providerNames) {
|
||||
const providerTitle = providerName // displayInfoOfProviderName(providerName).title.toLowerCase() // looks better lowercase, best practice to not use raw providerName
|
||||
if (!newSettingsOfProvider[providerName]._didFillInProviderSettings) continue // if disabled, don't display model options
|
||||
for (const { modelName, isHidden } of newSettingsOfProvider[providerName].models) {
|
||||
if (isHidden) continue
|
||||
newModelOptions.push({ name: `${modelName} (${providerTitle})`, selection: { providerName, modelName } })
|
||||
const showProviderInName = (visibleModelNameCounts.get(modelName) ?? 0) > 1
|
||||
const name = showProviderInName ? `${modelName} (${providerTitle})` : modelName
|
||||
newModelOptions.push({ name, selection: { providerName, modelName } })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -345,6 +371,10 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService {
|
|||
readS.settingsOfProvider[providerName].models,
|
||||
)
|
||||
}
|
||||
readS.settingsOfProvider[providerName].models = dedupeProviderModels(
|
||||
providerName,
|
||||
readS.settingsOfProvider[providerName].models,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -521,7 +551,7 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService {
|
|||
const oldModelNames = models.map(m => m.modelName)
|
||||
|
||||
const normalizedNames = normalizeAutodetectedModelNamesForProvider(providerName, autodetectedModelNames)
|
||||
let newModels = _modelsWithSwappedInNewModels({ existingModels: models, models: normalizedNames, type: 'autodetected' })
|
||||
let newModels = _modelsWithSwappedInNewModels({ providerName, existingModels: models, models: normalizedNames, type: 'autodetected' })
|
||||
if (providerName === 'mlx' || providerName === 'appleFoundationModels') {
|
||||
newModels = consolidateSingleAutodetectedProviderModels(providerName, newModels)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue