fix: describe saved fixed project models accurately

Projects with a saved fixed Runtime model could incorrectly claim to follow
the global Runtime configuration. Restore the fixed-model description while
keeping the simplified menu and the ability to return to inherited settings.
Model resolution, permissions, and remote Agent behavior are unchanged.

Validation: 3911 tests passed, 66 skipped; typecheck, lint, and release-note
validation passed. Added bilingual regressions for all three Agent Runtimes.

Release note: 项目已固定 Runtime 模型时,现在明确显示所用固定模型,不再误报为跟随全局设置。
This commit is contained in:
mesalogo
2026-09-11 01:21:24 +08:00
parent 073eb0d2f5
commit b2c85b8ece
6 changed files with 81 additions and 0 deletions
@@ -0,0 +1,51 @@
import { cleanup, fireEvent, render, screen } from '@testing-library/react'
import { afterEach, describe, expect, it, vi } from 'vitest'
import { defaultRuntimeSettings, type RuntimeSettings } from '../../shared/contracts'
import { ProjectRuntimeSelector } from './ProjectRuntimeSelector'
import i18n from './i18n'
afterEach(cleanup)
describe('ProjectRuntimeSelector saved model references', () => {
it.each(['zh-CN', 'en-US'])('describes fixed selections accurately in %s without restoring duplicate menus', async (language) => {
await i18n.changeLanguage(language)
const profileId = '00000000-0000-4000-8000-000000000011'
const settings = {
...defaultRuntimeSettings,
defaultModelProfileId: profileId,
modelProfiles: [{
id: profileId, name: 'Model A', modelName: 'model-a',
baseUrl: 'https://example.com/v1', protocol: 'openai-chat-completions',
authentication: 'api-key', apiKeyConfigured: true
}]
} as RuntimeSettings
const onChange = vi.fn()
for (const provider of ['opencode', 'continue', 'deepseek-harness'] as const) {
const runtime = provider === 'opencode' ? 'OpenCode'
: provider === 'continue' ? 'Continue' : 'DeepSeek Harness'
const props = {
ariaLabel: 'Project Runtime', label: 'Runtime', onChange,
runtimeSettings: settings
}
const view = render(<ProjectRuntimeSelector {...props} selection={{ provider, profileId }} />)
const select = screen.getByRole('combobox')
expect(select).toHaveValue(`${provider}:${profileId}`)
expect(screen.getByText(language === 'zh-CN'
? `通过 ${runtime} 运行,固定使用 Model A。`
: `Run through ${runtime} with the fixed model Model A.`)).toBeInTheDocument()
expect(screen.getAllByRole('option').filter((option) =>
(option as HTMLOptionElement).value.endsWith(`:${profileId}`))).toHaveLength(2)
fireEvent.change(select, { target: { value: `${provider}:default` } })
expect(onChange).toHaveBeenLastCalledWith({ provider })
view.rerender(<ProjectRuntimeSelector {...props} selection={{ provider }} />)
expect(select).toHaveValue(`${provider}:default`)
expect(screen.queryByText(language === 'zh-CN'
? `通过 ${runtime} 运行,固定使用 Model A。`
: `Run through ${runtime} with the fixed model Model A.`)).not.toBeInTheDocument()
expect(screen.getAllByRole('option').filter((option) =>
(option as HTMLOptionElement).value.endsWith(`:${profileId}`))).toHaveLength(1)
view.unmount()
}
})
})
@@ -54,6 +54,15 @@ function runtimeSelectionDescription(
: selection.provider === 'continue'
? 'Continue'
: 'DeepSeek Harness'
if (selection.profileId) {
const profile = settings.modelProfiles.find(
(candidate) => candidate.id === selection.profileId
)
return t('channels.project.fixedRuntimeDescription', {
runtime: runtimeLabel,
name: profile?.name ?? t('channels.project.missingProfile')
})
}
return t('channels.project.runtimeDescription', {
runtime: runtimeLabel
})
@@ -46,6 +46,7 @@ export const integrations = {
directDescription:
'Process messages directly with {{name}} ({{modelName}}).',
defaultDirect: 'Direct · Follow the global default model',
fixedRuntimeDescription: 'Run through {{runtime}} with the fixed model {{name}}.',
automaticDescription:
'Follow the global default Runtime setting.',
runtimeDescription:
@@ -40,6 +40,7 @@ export const integrations = {
'直接使用 {{name}}({{modelName}})处理消息。',
automaticDescription: '跟随全局 Runtime 默认设置',
defaultDirect: '直连 · 跟随全局默认模型',
fixedRuntimeDescription: '通过 {{runtime}} 运行,固定使用 {{name}}。',
runtimeDescription:
'通过 {{runtime}} Agent Runtime 运行,并跟随“Agent Runtime”设置中的全局 {{runtime}} 配置。',
defaultMode: '默认模式',