fix(server): include configured role models (#1024)

This commit is contained in:
程序员阿江(Relakkes) 2026-07-17 22:48:16 +08:00
parent e2e164da69
commit b7f1dd6d80
2 changed files with 61 additions and 6 deletions

View File

@ -48,6 +48,7 @@ let originalAnthropicModel: string | undefined
let originalAnthropicDefaultHaikuModel: string | undefined
let originalAnthropicDefaultSonnetModel: string | undefined
let originalAnthropicDefaultOpusModel: string | undefined
let originalAnthropicDefaultFableModel: string | undefined
async function setup() {
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'claude-test-'))
@ -66,6 +67,7 @@ async function setup() {
originalAnthropicDefaultHaikuModel = process.env.ANTHROPIC_DEFAULT_HAIKU_MODEL
originalAnthropicDefaultSonnetModel = process.env.ANTHROPIC_DEFAULT_SONNET_MODEL
originalAnthropicDefaultOpusModel = process.env.ANTHROPIC_DEFAULT_OPUS_MODEL
originalAnthropicDefaultFableModel = process.env.ANTHROPIC_DEFAULT_FABLE_MODEL
process.env.CLAUDE_CONFIG_DIR = tmpDir
process.env.HOME = tmpDir
process.env.USERPROFILE = tmpDir
@ -77,6 +79,7 @@ async function setup() {
delete process.env.ANTHROPIC_DEFAULT_HAIKU_MODEL
delete process.env.ANTHROPIC_DEFAULT_SONNET_MODEL
delete process.env.ANTHROPIC_DEFAULT_OPUS_MODEL
delete process.env.ANTHROPIC_DEFAULT_FABLE_MODEL
clearKeychainCache()
primeKeychainCacheFromPrefetch(null)
clearOpenAIOAuthTokenCache()
@ -162,6 +165,12 @@ async function teardown() {
delete process.env.ANTHROPIC_DEFAULT_OPUS_MODEL
}
if (originalAnthropicDefaultFableModel !== undefined) {
process.env.ANTHROPIC_DEFAULT_FABLE_MODEL = originalAnthropicDefaultFableModel
} else {
delete process.env.ANTHROPIC_DEFAULT_FABLE_MODEL
}
await fs.rm(tmpDir, { recursive: true, force: true })
}
@ -676,6 +685,31 @@ describe('Models API', () => {
expect(ids.filter((id: string) => id === 'deepseek-v4-pro')).toHaveLength(1)
})
it('GET /api/models should merge user settings model roles with runtime env and include Fable', async () => {
await new SettingsService().updateUserSettings({
env: {
ANTHROPIC_MODEL: 'claude-opus-4-8',
ANTHROPIC_DEFAULT_HAIKU_MODEL: 'claude-haiku-4-5-20251001',
ANTHROPIC_DEFAULT_SONNET_MODEL: 'claude-sonnet-4-6',
ANTHROPIC_DEFAULT_OPUS_MODEL: 'claude-opus-4-8',
ANTHROPIC_DEFAULT_FABLE_MODEL: 'claude-fable-5',
},
})
process.env.ANTHROPIC_MODEL = 'claude-opus-4-8'
const { req, url, segments } = makeRequest('GET', '/api/models')
const res = await handleModelsApi(req, url, segments)
expect(res.status).toBe(200)
const body = await res.json()
expect(body.models.map((model: { id: string }) => model.id)).toEqual([
'claude-opus-4-8',
'claude-haiku-4-5-20251001',
'claude-sonnet-4-6',
'claude-fable-5',
])
})
it('GET /api/models/current should return default model when not set', async () => {
const { req, url, segments } = makeRequest('GET', '/api/models/current')
const res = await handleModelsApi(req, url, segments)

View File

@ -95,6 +95,7 @@ function buildProviderModelList(models: {
haiku: string
sonnet: string
opus: string
fable?: string
}): ApiModelInfo[] {
const modelList: ApiModelInfo[] = []
@ -128,6 +129,14 @@ function buildProviderModelList(models: {
context: '',
}
: null)
addUniqueModel(modelList, models.fable
? {
id: models.fable,
name: models.fable,
description: 'Fable model',
context: '',
}
: null)
return modelList
}
@ -170,12 +179,20 @@ async function getGrokModelList(): Promise<ApiModelInfo[]> {
}))
}
function getEnvConfiguredAnthropicModels(): ApiModelInfo[] {
function getConfiguredAnthropicModels(settingsEnv: Record<string, unknown>): ApiModelInfo[] {
const resolveModel = (key: string): string => {
const runtimeValue = process.env[key]?.trim()
if (runtimeValue) return runtimeValue
const settingsValue = settingsEnv[key]
return typeof settingsValue === 'string' ? settingsValue.trim() : ''
}
return buildProviderModelList({
main: process.env.ANTHROPIC_MODEL?.trim() || '',
haiku: process.env.ANTHROPIC_DEFAULT_HAIKU_MODEL?.trim() || '',
sonnet: process.env.ANTHROPIC_DEFAULT_SONNET_MODEL?.trim() || '',
opus: process.env.ANTHROPIC_DEFAULT_OPUS_MODEL?.trim() || '',
main: resolveModel('ANTHROPIC_MODEL'),
haiku: resolveModel('ANTHROPIC_DEFAULT_HAIKU_MODEL'),
sonnet: resolveModel('ANTHROPIC_DEFAULT_SONNET_MODEL'),
opus: resolveModel('ANTHROPIC_DEFAULT_OPUS_MODEL'),
fable: resolveModel('ANTHROPIC_DEFAULT_FABLE_MODEL'),
})
}
@ -188,7 +205,11 @@ async function getOpenAIAuthModels(): Promise<ApiModelInfo[]> {
}
async function getStandaloneModelList(): Promise<ApiModelInfo[]> {
const models = [...getEnvConfiguredAnthropicModels()]
const settings = await settingsService.getUserSettings()
const settingsEnv = settings.env && typeof settings.env === 'object' && !Array.isArray(settings.env)
? settings.env as Record<string, unknown>
: {}
const models = [...getConfiguredAnthropicModels(settingsEnv)]
if (models.length === 0) {
models.push(...DEFAULT_MODELS)