feat: simplify DeepSeek Harness model selection
- replace competing model-source choices with one DeepSeek connection picker - keep administrator-provided environment settings compatible without exposing them as a selectable UI source - update bilingual guidance, tests, and runtime design documentation
This commit is contained in:
@@ -403,6 +403,7 @@ DeepSeek Harness 首版只使用 GoodBuddy 模型连接:
|
|||||||
- 服务地址必须是 `https://api.deepseek.com`,且不得包含用户信息。
|
- 服务地址必须是 `https://api.deepseek.com`,且不得包含用户信息。
|
||||||
- 模型名称和服务地址由 Main 传入受控 Host。
|
- 模型名称和服务地址由 Main 传入受控 Host。
|
||||||
- API Key 继续保存在 GoodBuddy 加密设置中。
|
- API Key 继续保存在 GoodBuddy 加密设置中。
|
||||||
|
- 启动环境提供的部署连接只由 Main 自动解析,不在 Renderer 中显示为可选来源。
|
||||||
|
|
||||||
不允许选择 Harness 自有的用户配置文件或自定义 Host。Runtime 始终使用随当前 GoodBuddy 版本发布的内置 Host,并通过完整内部能力握手。
|
不允许选择 Harness 自有的用户配置文件或自定义 Host。Runtime 始终使用随当前 GoodBuddy 版本发布的内置 Host,并通过完整内部能力握手。
|
||||||
|
|
||||||
|
|||||||
@@ -1372,7 +1372,7 @@ describe('SettingsPanel runtime files', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('configures DeepSeek Harness only with Chat Completions or platform settings', async () => {
|
it('configures DeepSeek Harness with a compatible GoodBuddy connection', async () => {
|
||||||
const harnessProfileId =
|
const harnessProfileId =
|
||||||
'00000000-0000-4000-8000-000000000051'
|
'00000000-0000-4000-8000-000000000051'
|
||||||
getRuntime.mockResolvedValueOnce({
|
getRuntime.mockResolvedValueOnce({
|
||||||
@@ -1451,8 +1451,12 @@ describe('SettingsPanel runtime files', () => {
|
|||||||
})
|
})
|
||||||
).not.toBeInTheDocument()
|
).not.toBeInTheDocument()
|
||||||
const source = screen.getByLabelText(
|
const source = screen.getByLabelText(
|
||||||
'DeepSeek Harness GoodBuddy 模型连接'
|
'DeepSeek Harness DeepSeek 模型连接'
|
||||||
)
|
)
|
||||||
|
expect(screen.queryByRole('radio')).not.toBeInTheDocument()
|
||||||
|
expect(
|
||||||
|
screen.queryByText('使用平台 DeepSeek 环境配置')
|
||||||
|
).not.toBeInTheDocument()
|
||||||
expect(
|
expect(
|
||||||
within(source).getByRole('option', { name: '默认模型(不兼容)' })
|
within(source).getByRole('option', { name: '默认模型(不兼容)' })
|
||||||
).toBeDisabled()
|
).toBeDisabled()
|
||||||
@@ -1460,22 +1464,20 @@ describe('SettingsPanel runtime files', () => {
|
|||||||
within(source).getByRole('option', { name: 'DeepSeek Chat' })
|
within(source).getByRole('option', { name: 'DeepSeek Chat' })
|
||||||
).not.toBeDisabled()
|
).not.toBeDisabled()
|
||||||
|
|
||||||
fireEvent.click(
|
|
||||||
screen.getByRole('radio', {
|
|
||||||
name: /使用平台 DeepSeek 环境配置/
|
|
||||||
})
|
|
||||||
)
|
|
||||||
fireEvent.click(screen.getByRole('button', { name: '保存设置' }))
|
fireEvent.click(screen.getByRole('button', { name: '保存设置' }))
|
||||||
await waitFor(() =>
|
await waitFor(() =>
|
||||||
expect(updateRuntime).toHaveBeenCalledWith(
|
expect(updateRuntime).toHaveBeenCalledWith(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
deepseekHarnessModelSource: { kind: 'platform' }
|
deepseekHarnessModelSource: {
|
||||||
|
kind: 'profile',
|
||||||
|
profileId: harnessProfileId
|
||||||
|
}
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('normalizes an environment-managed DeepSeek profile to the platform source when saving', async () => {
|
it('keeps an environment-managed source compatible without exposing it as an option', async () => {
|
||||||
getRuntime.mockResolvedValueOnce({
|
getRuntime.mockResolvedValueOnce({
|
||||||
...runtimeSettings,
|
...runtimeSettings,
|
||||||
modelBaseUrl: 'https://api.deepseek.com',
|
modelBaseUrl: 'https://api.deepseek.com',
|
||||||
@@ -1527,11 +1529,17 @@ describe('SettingsPanel runtime files', () => {
|
|||||||
name: 'DeepSeek Harness(预览)'
|
name: 'DeepSeek Harness(预览)'
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
fireEvent.click(
|
expect(screen.queryByRole('radio')).not.toBeInTheDocument()
|
||||||
screen.getByRole('radio', {
|
expect(
|
||||||
name: /使用 GoodBuddy 模型连接/
|
screen.getByText('管理员预置的 DeepSeek 连接')
|
||||||
})
|
).toBeInTheDocument()
|
||||||
|
const source = screen.getByLabelText(
|
||||||
|
'DeepSeek Harness DeepSeek 模型连接'
|
||||||
)
|
)
|
||||||
|
expect(source).toHaveValue('')
|
||||||
|
fireEvent.change(source, {
|
||||||
|
target: { value: runtimeSettings.modelProfiles[0]!.id }
|
||||||
|
})
|
||||||
fireEvent.click(screen.getByRole('button', { name: '保存设置' }))
|
fireEvent.click(screen.getByRole('button', { name: '保存设置' }))
|
||||||
|
|
||||||
await waitFor(() =>
|
await waitFor(() =>
|
||||||
|
|||||||
@@ -1981,7 +1981,7 @@ export function SettingsPanel({
|
|||||||
detecting={detecting}
|
detecting={detecting}
|
||||||
modelConfiguration={
|
modelConfiguration={
|
||||||
activeRuntimeModelSource.kind === 'platform'
|
activeRuntimeModelSource.kind === 'platform'
|
||||||
? t('runtime.deepseekHarness.platformSource')
|
? t('runtime.deepseekHarness.managedSource')
|
||||||
: activeRuntimeModelProfile
|
: activeRuntimeModelProfile
|
||||||
? t('runtime.followGoodBuddy', {
|
? t('runtime.followGoodBuddy', {
|
||||||
name: modelProfileDisplayName(
|
name: modelProfileDisplayName(
|
||||||
@@ -1998,100 +1998,48 @@ export function SettingsPanel({
|
|||||||
runtime: 'DeepSeek Harness'
|
runtime: 'DeepSeek Harness'
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
<fieldset className="runtime-source-options">
|
<label className="field">
|
||||||
<legend>{t('runtime.sourceLegend')}</legend>
|
<span>{t('runtime.deepseekHarness.connection')}</span>
|
||||||
<label>
|
<select
|
||||||
<input
|
aria-label={`DeepSeek Harness ${t(
|
||||||
checked={
|
'runtime.deepseekHarness.connection'
|
||||||
deepseekHarnessModelSource.kind === 'profile'
|
)}`}
|
||||||
}
|
disabled={!defaultDeepseekHarnessModelProfile}
|
||||||
disabled={!defaultDeepseekHarnessModelProfile}
|
onChange={(event) =>
|
||||||
name="deepseek-harness-model-source"
|
setDeepseekHarnessModelSource(
|
||||||
onChange={() => {
|
parseModelSource(event.target.value)
|
||||||
if (defaultDeepseekHarnessModelProfile) {
|
)
|
||||||
setDeepseekHarnessModelSource({
|
}
|
||||||
kind: 'profile',
|
value={
|
||||||
profileId:
|
deepseekHarnessModelSource.kind === 'profile'
|
||||||
defaultDeepseekHarnessModelProfile.id
|
? deepseekHarnessModelSource.profileId
|
||||||
})
|
: ''
|
||||||
}
|
}
|
||||||
}}
|
>
|
||||||
type="radio"
|
<option disabled value="">
|
||||||
/>
|
|
||||||
<span>
|
|
||||||
<strong>
|
|
||||||
{t(
|
|
||||||
'runtime.deepseekHarness.goodBuddySource'
|
|
||||||
)}
|
|
||||||
</strong>
|
|
||||||
<small>
|
|
||||||
{t(
|
|
||||||
'runtime.deepseekHarness.goodBuddySourceDescription'
|
|
||||||
)}
|
|
||||||
</small>
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
<label>
|
|
||||||
<input
|
|
||||||
checked={
|
|
||||||
deepseekHarnessModelSource.kind === 'platform'
|
|
||||||
}
|
|
||||||
name="deepseek-harness-model-source"
|
|
||||||
onChange={() =>
|
|
||||||
setDeepseekHarnessModelSource({
|
|
||||||
kind: 'platform'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
type="radio"
|
|
||||||
/>
|
|
||||||
<span>
|
|
||||||
<strong>
|
|
||||||
{t('runtime.deepseekHarness.platformSource')}
|
|
||||||
</strong>
|
|
||||||
<small>
|
|
||||||
{t(
|
|
||||||
'runtime.deepseekHarness.platformSourceDescription'
|
|
||||||
)}
|
|
||||||
</small>
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
</fieldset>
|
|
||||||
{deepseekHarnessModelSource.kind === 'profile' && (
|
|
||||||
<label className="field">
|
|
||||||
<span>{t('runtime.goodBuddyConnection')}</span>
|
|
||||||
<select
|
|
||||||
aria-label={`DeepSeek Harness ${t(
|
|
||||||
'runtime.goodBuddyConnection'
|
|
||||||
)}`}
|
|
||||||
onChange={(event) =>
|
|
||||||
setDeepseekHarnessModelSource(
|
|
||||||
parseModelSource(event.target.value)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
value={deepseekHarnessModelSource.profileId}
|
|
||||||
>
|
|
||||||
{modelProfiles.map((profile) => (
|
|
||||||
<option
|
|
||||||
disabled={
|
|
||||||
!isDeepseekHarnessCompatible(profile)
|
|
||||||
}
|
|
||||||
key={profile.id}
|
|
||||||
value={profile.id}
|
|
||||||
>
|
|
||||||
{modelProfileDisplayName(profile)}
|
|
||||||
{isDeepseekHarnessCompatible(profile)
|
|
||||||
? ''
|
|
||||||
: t('runtime.incompatibleSuffix')}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
<small>
|
|
||||||
{t(
|
{t(
|
||||||
'runtime.deepseekHarness.connectionDescription'
|
'runtime.deepseekHarness.connectionPlaceholder'
|
||||||
)}
|
)}
|
||||||
</small>
|
</option>
|
||||||
</label>
|
{modelProfiles.map((profile) => (
|
||||||
)}
|
<option
|
||||||
|
disabled={!isDeepseekHarnessCompatible(profile)}
|
||||||
|
key={profile.id}
|
||||||
|
value={profile.id}
|
||||||
|
>
|
||||||
|
{modelProfileDisplayName(profile)}
|
||||||
|
{isDeepseekHarnessCompatible(profile)
|
||||||
|
? ''
|
||||||
|
: t('runtime.incompatibleSuffix')}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
<small>
|
||||||
|
{t(
|
||||||
|
'runtime.deepseekHarness.connectionDescription'
|
||||||
|
)}
|
||||||
|
</small>
|
||||||
|
</label>
|
||||||
<details className="settings-section">
|
<details className="settings-section">
|
||||||
<summary>{t('runtime.advanced')}</summary>
|
<summary>{t('runtime.advanced')}</summary>
|
||||||
<p className="settings-panel__description">
|
<p className="settings-panel__description">
|
||||||
|
|||||||
@@ -242,14 +242,11 @@ export const settings = {
|
|||||||
'DeepSeek Harness currently supports DeepSeek models only and is not intended for other model providers.',
|
'DeepSeek Harness currently supports DeepSeek models only and is not intended for other model providers.',
|
||||||
description:
|
description:
|
||||||
'GoodBuddy maintains the fixed Host and control protocol internally and uses pinned Harness libraries underneath. Execute tool calls receive automatic one-time authorization, Ask remains read-only, and cancellation and workspace safety boundaries remain in place. It does not integrate with the DSH plugin or marketplace mechanisms.',
|
'GoodBuddy maintains the fixed Host and control protocol internally and uses pinned Harness libraries underneath. Execute tool calls receive automatic one-time authorization, Ask remains read-only, and cancellation and workspace safety boundaries remain in place. It does not integrate with the DSH plugin or marketplace mechanisms.',
|
||||||
goodBuddySource: 'Use a GoodBuddy model connection',
|
managedSource: 'Administrator-provided DeepSeek connection',
|
||||||
goodBuddySourceDescription:
|
connection: 'DeepSeek model connection',
|
||||||
'Only OpenAI-compatible Chat Completions connections are available. The connection must point to a DeepSeek model.',
|
connectionPlaceholder: 'Select a DeepSeek model connection',
|
||||||
platformSource: 'Use platform DeepSeek environment settings',
|
|
||||||
platformSourceDescription:
|
|
||||||
'Reads platform-managed DeepSeek settings from the launch environment without exposing credentials to the renderer.',
|
|
||||||
connectionDescription:
|
connectionDescription:
|
||||||
'The internal GoodBuddy Harness Runtime currently accepts only the OpenAI-compatible Chat Completions protocol.',
|
'Choose a GoodBuddy model connection. Only OpenAI-compatible Chat Completions connections that point to DeepSeek are supported.',
|
||||||
advancedDescription:
|
advancedDescription:
|
||||||
'This Runtime always uses GoodBuddy’s bundled, version-pinned Host. It does not load external DSH plugins, marketplace packages, user profiles, or custom Hosts.'
|
'This Runtime always uses GoodBuddy’s bundled, version-pinned Host. It does not load external DSH plugins, marketplace packages, user profiles, or custom Hosts.'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -220,14 +220,11 @@ export const settings = {
|
|||||||
'DeepSeek Harness 当前仅支持 DeepSeek 模型,不适用于其他模型提供商。',
|
'DeepSeek Harness 当前仅支持 DeepSeek 模型,不适用于其他模型提供商。',
|
||||||
description:
|
description:
|
||||||
'由 GoodBuddy 内部维护固定 Host 与控制协议,复用锁定的 Harness 底层库;Execute 工具调用自动单次授权,Ask 保持只读,并保留取消和工作区安全边界;不接入 DSH 插件或市场机制。',
|
'由 GoodBuddy 内部维护固定 Host 与控制协议,复用锁定的 Harness 底层库;Execute 工具调用自动单次授权,Ask 保持只读,并保留取消和工作区安全边界;不接入 DSH 插件或市场机制。',
|
||||||
goodBuddySource: '使用 GoodBuddy 模型连接',
|
managedSource: '管理员预置的 DeepSeek 连接',
|
||||||
goodBuddySourceDescription:
|
connection: 'DeepSeek 模型连接',
|
||||||
'只能选择 OpenAI 兼容 Chat Completions 连接;该连接必须指向 DeepSeek 模型。',
|
connectionPlaceholder: '选择 DeepSeek 模型连接',
|
||||||
platformSource: '使用平台 DeepSeek 环境配置',
|
|
||||||
platformSourceDescription:
|
|
||||||
'从启动环境读取平台管理的 DeepSeek 配置,不会在渲染进程中显示凭据。',
|
|
||||||
connectionDescription:
|
connectionDescription:
|
||||||
'GoodBuddy 内部 Harness Runtime 目前仅接受 OpenAI 兼容 Chat Completions 协议。',
|
'从 GoodBuddy 模型连接中选择;仅支持指向 DeepSeek 的 OpenAI 兼容 Chat Completions 连接。',
|
||||||
advancedDescription:
|
advancedDescription:
|
||||||
'该 Runtime 始终使用 GoodBuddy 内置并固定版本的 Host,不加载外部 DSH 插件、市场包、用户 profile 或自定义 Host。'
|
'该 Runtime 始终使用 GoodBuddy 内置并固定版本的 Host,不加载外部 DSH 插件、市场包、用户 profile 或自定义 Host。'
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user