fix: 修复 model alias 导致无限递归栈溢出 (cherry-pick cee62bc)
This commit is contained in:
parent
f484c9fa7c
commit
aa72a8ed6c
|
|
@ -28,6 +28,18 @@ import { getAPIProvider } from './providers.js'
|
||||||
import { LIGHTNING_BOLT } from '../../constants/figures.js'
|
import { LIGHTNING_BOLT } from '../../constants/figures.js'
|
||||||
import { isModelAllowed } from './modelAllowlist.js'
|
import { isModelAllowed } from './modelAllowlist.js'
|
||||||
import { type ModelAlias, isModelAlias } from './aliases.js'
|
import { type ModelAlias, isModelAlias } from './aliases.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the value is a model alias or a model alias with a suffix
|
||||||
|
* like [1m] (e.g. "opus", "opus[1m]", "sonnet", "haiku[1m]").
|
||||||
|
* Used to guard against infinite recursion when getDefault*Model() falls back
|
||||||
|
* to the user-specified setting — an alias like "opus[1m]" would cause
|
||||||
|
* parseUserSpecifiedModel → getDefaultOpusModel → parseUserSpecifiedModel loop.
|
||||||
|
*/
|
||||||
|
function isAliasOrAliasWithSuffix(value: string): boolean {
|
||||||
|
const base = value.replace(/\[1m\]$/i, '').trim()
|
||||||
|
return isModelAlias(base)
|
||||||
|
}
|
||||||
import { capitalize } from '../stringUtils.js'
|
import { capitalize } from '../stringUtils.js'
|
||||||
|
|
||||||
export type ModelShortName = string
|
export type ModelShortName = string
|
||||||
|
|
@ -111,19 +123,6 @@ export function getBestModel(): ModelName {
|
||||||
return getDefaultOpusModel()
|
return getDefaultOpusModel()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolve the provider's primary model from its env var (e.g. OPENAI_MODEL).
|
|
||||||
* Returns undefined for providers that don't have a primary-model env var
|
|
||||||
* (Bedrock, Vertex, Foundry, firstParty).
|
|
||||||
*/
|
|
||||||
function getProviderPrimaryModel(): ModelName | undefined {
|
|
||||||
const provider = getAPIProvider()
|
|
||||||
if (provider === 'openai') return process.env.OPENAI_MODEL
|
|
||||||
if (provider === 'gemini') return process.env.GEMINI_MODEL
|
|
||||||
if (provider === 'grok') return process.env.GROK_MODEL
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
// @[MODEL LAUNCH]: Update the default Opus model (3P providers may lag so keep defaults unchanged).
|
// @[MODEL LAUNCH]: Update the default Opus model (3P providers may lag so keep defaults unchanged).
|
||||||
export function getDefaultOpusModel(): ModelName {
|
export function getDefaultOpusModel(): ModelName {
|
||||||
const provider = getAPIProvider()
|
const provider = getAPIProvider()
|
||||||
|
|
@ -139,12 +138,14 @@ export function getDefaultOpusModel(): ModelName {
|
||||||
if (process.env.ANTHROPIC_DEFAULT_OPUS_MODEL) {
|
if (process.env.ANTHROPIC_DEFAULT_OPUS_MODEL) {
|
||||||
return process.env.ANTHROPIC_DEFAULT_OPUS_MODEL
|
return process.env.ANTHROPIC_DEFAULT_OPUS_MODEL
|
||||||
}
|
}
|
||||||
// 3P providers: if user set a primary model (e.g. OPENAI_MODEL=deepseek-v4-pro),
|
// Fall back to user's configured model — custom providers may not
|
||||||
// fall back to it instead of a hardcoded Anthropic model. This prevents
|
// recognize hardcoded Anthropic model IDs.
|
||||||
// sideQuery / background tasks from sending requests to Anthropic's API
|
// Skip if the user setting is a model alias (e.g. "opus", "opus[1m]") to
|
||||||
// when the user configured a third-party provider.
|
// avoid infinite recursion: parseUserSpecifiedModel(alias) → getDefaultOpusModel().
|
||||||
const primaryModel = getProviderPrimaryModel()
|
const userSpecifiedOpus = getUserSpecifiedModelSetting()
|
||||||
if (primaryModel) return primaryModel
|
if (userSpecifiedOpus && !isAliasOrAliasWithSuffix(userSpecifiedOpus)) {
|
||||||
|
return parseUserSpecifiedModel(userSpecifiedOpus)
|
||||||
|
}
|
||||||
// 3P providers (Bedrock, Vertex, Foundry) — kept as a separate branch
|
// 3P providers (Bedrock, Vertex, Foundry) — kept as a separate branch
|
||||||
// even when values match, since 3P availability lags firstParty and
|
// even when values match, since 3P availability lags firstParty and
|
||||||
// these will diverge again at the next model launch.
|
// these will diverge again at the next model launch.
|
||||||
|
|
@ -158,10 +159,7 @@ export function getDefaultOpusModel(): ModelName {
|
||||||
export function getDefaultSonnetModel(): ModelName {
|
export function getDefaultSonnetModel(): ModelName {
|
||||||
const provider = getAPIProvider()
|
const provider = getAPIProvider()
|
||||||
// For OpenAI provider, check OPENAI_DEFAULT_SONNET_MODEL first
|
// For OpenAI provider, check OPENAI_DEFAULT_SONNET_MODEL first
|
||||||
if (
|
if (provider === 'openai' && process.env.OPENAI_DEFAULT_SONNET_MODEL) {
|
||||||
provider === 'openai' &&
|
|
||||||
process.env.OPENAI_DEFAULT_SONNET_MODEL
|
|
||||||
) {
|
|
||||||
return process.env.OPENAI_DEFAULT_SONNET_MODEL
|
return process.env.OPENAI_DEFAULT_SONNET_MODEL
|
||||||
}
|
}
|
||||||
// For Gemini provider, check GEMINI_DEFAULT_SONNET_MODEL
|
// For Gemini provider, check GEMINI_DEFAULT_SONNET_MODEL
|
||||||
|
|
@ -172,10 +170,14 @@ export function getDefaultSonnetModel(): ModelName {
|
||||||
if (process.env.ANTHROPIC_DEFAULT_SONNET_MODEL) {
|
if (process.env.ANTHROPIC_DEFAULT_SONNET_MODEL) {
|
||||||
return process.env.ANTHROPIC_DEFAULT_SONNET_MODEL
|
return process.env.ANTHROPIC_DEFAULT_SONNET_MODEL
|
||||||
}
|
}
|
||||||
// 3P providers: fall back to user's primary model instead of a hardcoded
|
// Fall back to user's configured model (ANTHROPIC_MODEL / settings) —
|
||||||
// Anthropic model name.
|
// custom providers (proxies, national clouds) may not recognize the
|
||||||
const primaryModel = getProviderPrimaryModel()
|
// hardcoded Anthropic model IDs.
|
||||||
if (primaryModel) return primaryModel
|
// Skip if the user setting is a model alias to avoid infinite recursion.
|
||||||
|
const userSpecified = getUserSpecifiedModelSetting()
|
||||||
|
if (userSpecified && !isAliasOrAliasWithSuffix(userSpecified)) {
|
||||||
|
return parseUserSpecifiedModel(userSpecified)
|
||||||
|
}
|
||||||
// Default to Sonnet 4.5 for 3P since they may not have 4.6 yet
|
// Default to Sonnet 4.5 for 3P since they may not have 4.6 yet
|
||||||
if (provider !== 'firstParty') {
|
if (provider !== 'firstParty') {
|
||||||
return getModelStrings().sonnet45
|
return getModelStrings().sonnet45
|
||||||
|
|
@ -198,11 +200,13 @@ export function getDefaultHaikuModel(): ModelName {
|
||||||
if (process.env.ANTHROPIC_DEFAULT_HAIKU_MODEL) {
|
if (process.env.ANTHROPIC_DEFAULT_HAIKU_MODEL) {
|
||||||
return process.env.ANTHROPIC_DEFAULT_HAIKU_MODEL
|
return process.env.ANTHROPIC_DEFAULT_HAIKU_MODEL
|
||||||
}
|
}
|
||||||
|
// Fall back to user's configured model — custom providers may not
|
||||||
// 3P providers: fall back to user's primary model instead of a hardcoded
|
// recognize hardcoded Anthropic model IDs.
|
||||||
// Anthropic model name.
|
// Skip if the user setting is a model alias to avoid infinite recursion.
|
||||||
const primaryModel = getProviderPrimaryModel()
|
const userSpecifiedHaiku = getUserSpecifiedModelSetting()
|
||||||
if (primaryModel) return primaryModel
|
if (userSpecifiedHaiku && !isAliasOrAliasWithSuffix(userSpecifiedHaiku)) {
|
||||||
|
return parseUserSpecifiedModel(userSpecifiedHaiku)
|
||||||
|
}
|
||||||
|
|
||||||
// Haiku 4.5 is available on all platforms (first-party, Foundry, Bedrock, Vertex)
|
// Haiku 4.5 is available on all platforms (first-party, Foundry, Bedrock, Vertex)
|
||||||
return getModelStrings().haiku45
|
return getModelStrings().haiku45
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user