diff --git a/packages/client/ui-model-selection/README.i18n.yaml b/packages/client/ui-model-selection/README.i18n.yaml index 2495b842d6..943d65c48f 100644 --- a/packages/client/ui-model-selection/README.i18n.yaml +++ b/packages/client/ui-model-selection/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/ui-model-selection/README.md -README.md: 929d23ad4af1fe41e1cc7398ef432e73ca2a7a94 -README.zh.md: 23e786d9e490b0739498a3e83703b42ebc345cbd +README.md: 1cefc8b0b0a569f6bc20a0fedab692710b39df43 +README.zh.md: 272110006f58a5db70a439e78629a1675d349abf diff --git a/packages/client/ui-model-selection/README.md b/packages/client/ui-model-selection/README.md index 929d23ad4a..1cefc8b0b0 100644 --- a/packages/client/ui-model-selection/README.md +++ b/packages/client/ui-model-selection/README.md @@ -2,7 +2,7 @@ English | [中文](README.zh.md) -Model selection plugin, browser half: TWO entries over ONE per-session directory owned by `ModelDirectoryResolver` (`ctx.modelDirectories`). For ordinary sessions, the `/model` popupSelect contribution (registered through `ctx.commandUi`) and the composer's named `conversation.input.model` seat both load the session's advisory directory through `session.models` and submit through `session.selectModel` via the same `ModelDirectory` instance. The compact composer trigger opens a two-level Model/Effort menu: models stay provider-grouped, while the selected exact model supplies its adapter-owned effort names, descriptions, and default. `/model` applies the selected model's default effort, and the composer can then choose any advertised effort. +Model selection plugin, browser half: TWO entries over ONE per-session directory owned by `ModelDirectoryResolver` (`ctx.modelDirectories`). For ordinary sessions, the `/model` popupSelect contribution (registered through `ctx.commandUi`) and the composer's named `conversation.input.model` seat both load the session's advisory directory through `session.models` and submit through `session.selectModel` via the same `ModelDirectory` instance. The compact composer trigger opens a two-level Model/Effort menu: models stay provider-grouped, while the selected exact model supplies its adapter-owned effort names and default. The menu renders model and effort names only; catalog descriptions remain available to other consumers. `/model` applies the selected model's default effort, and the composer can then choose any advertised effort. The Host-reported provider/model/reasoning `ModelSelection` is the single selection fact, but it is echoed only when the exact provider/model pair remains in the advertised groups; an absent catalog row leaves the routable selection intact while the trigger prompts `Select model`, no stale row is synthesized, and no Effort row is shown until the user picks an advertised model. Directory loads and selections share a generation counter so an older response never overwrites a newer one; a connection reset drops every resident projection and repulls the Host-restored selection before display. Provider-local metadata failures list inline while usable groups stay selectable, and selection failures retain the prior selection and directory. diff --git a/packages/client/ui-model-selection/README.zh.md b/packages/client/ui-model-selection/README.zh.md index 23e786d9e4..272110006f 100644 --- a/packages/client/ui-model-selection/README.zh.md +++ b/packages/client/ui-model-selection/README.zh.md @@ -2,7 +2,7 @@ [English](README.md) | 中文 -模型选择插件(浏览器侧):**两个入口共用一份会话级目录**,由 `ModelDirectoryResolver`(`ctx.modelDirectories`)持有。对于普通会话,`/model` popupSelect 贡献项(经 `ctx.commandUi` 注册)与 composer 的具名 `conversation.input.model` slot 都通过同一个 `ModelDirectory` 实例,经 `session.models` 加载会话的建议目录,并经 `session.selectModel` 提交。紧凑型 composer 触发器会打开两级 Model/Effort 菜单:模型仍按提供方分组,所选具体模型则提供由其适配器持有的推理强度名称、说明和默认值。`/model` 应用所选模型的默认推理强度,composer 随后可以选择任一已公布的推理强度。 +模型选择插件(浏览器侧):**两个入口共用一份会话级目录**,由 `ModelDirectoryResolver`(`ctx.modelDirectories`)持有。对于普通会话,`/model` popupSelect 贡献项(经 `ctx.commandUi` 注册)与 composer 的具名 `conversation.input.model` slot 都通过同一个 `ModelDirectory` 实例,经 `session.models` 加载会话的建议目录,并经 `session.selectModel` 提交。紧凑型 composer 触发器会打开两级 Model/Effort 菜单:模型仍按提供方分组,所选具体模型则提供由其适配器持有的推理强度名称和默认值。菜单只呈现模型与推理强度名称;目录中的说明仍可供其他消费方使用。`/model` 应用所选模型的默认推理强度,composer 随后可以选择任一已公布的推理强度。 Host 报告的 `ModelSelection` 是唯一的选择事实,其中包含提供方、模型与推理(reasoning)强度;但只有当该提供方/模型对仍在已公布分组中时才会回显。目录行缺席时,可路由的选择保持不变,但触发器会提示 `Select model`;系统不会合成陈旧行,且在用户选择已公布的模型之前不会显示 Effort 行。目录加载与选择共享一个代次计数器,旧响应不会覆盖新结果;连接重置会丢弃所有常驻目录投影,并在显示前重新拉取 Host 恢复的选择。各提供方的元数据获取失败会内联列出,同时可用分组仍可选择;选择失败会保留先前的选择和目录。 diff --git a/packages/client/ui-model-selection/src/client/ModelSelect.module.css b/packages/client/ui-model-selection/src/client/ModelSelect.module.css index 76b9d3cb2a..492fb12f8c 100644 --- a/packages/client/ui-model-selection/src/client/ModelSelect.module.css +++ b/packages/client/ui-model-selection/src/client/ModelSelect.module.css @@ -207,15 +207,6 @@ white-space: nowrap; } -.description { - overflow: hidden; - color: var(--dsw-alias-label-tertiary); - font-size: 12px; - line-height: 18px; - text-overflow: ellipsis; - white-space: nowrap; -} - .check { display: grid; place-items: center; diff --git a/packages/client/ui-model-selection/src/client/ModelSelect.tsx b/packages/client/ui-model-selection/src/client/ModelSelect.tsx index aa21644e1d..e910651e86 100644 --- a/packages/client/ui-model-selection/src/client/ModelSelect.tsx +++ b/packages/client/ui-model-selection/src/client/ModelSelect.tsx @@ -33,7 +33,6 @@ interface EffortChoice { key: string effort: string | undefined label: string - description?: string } /** @@ -97,7 +96,6 @@ export function ModelSelect( key: `effort:${effort.id}`, effort: effort.id, label: effort.name, - ...effort.description === undefined ? {} : { description: effort.description }, })), ], [reasoning, t]) const busy = state.status === 'selecting' @@ -303,9 +301,6 @@ export function ModelSelect( > {model.name} - {model.description !== undefined && ( - {model.description} - )} {selected ? : null} @@ -346,9 +341,6 @@ export function ModelSelect( > {level.label} - {level.description !== undefined && ( - {level.description} - )} {effectiveEffort === level.effort ? : null} diff --git a/packages/client/ui-model-selection/tests/model-select.client.spec.tsx b/packages/client/ui-model-selection/tests/model-select.client.spec.tsx index eefd167c1b..a46cdcf212 100644 --- a/packages/client/ui-model-selection/tests/model-select.client.spec.tsx +++ b/packages/client/ui-model-selection/tests/model-select.client.spec.tsx @@ -36,7 +36,12 @@ function state(overrides: Partial = {}): ModelDirectoryStat groups: [{ id: 'deepseek-official', name: 'DeepSeek', - models: [{ id: 'deepseek-v4-flash', name: 'DeepSeek-V4-Flash', reasoning }], + models: [{ + id: 'deepseek-v4-flash', + name: 'DeepSeek-V4-Flash', + description: 'Fast catalog description', + reasoning, + }], }], failures: [], status: 'ready', @@ -48,7 +53,7 @@ function state(overrides: Partial = {}): ModelDirectoryStat afterEach(cleanup) describe('ModelSelect reasoning effort', () => { - it('renders adapter metadata and submits the effort as part of the session selection', async () => { + it('renders effort names without descriptions and submits the effort as part of the session selection', async () => { const directory = createSnapshotStore(state()) const select = vi.fn(async (selection: ModelSelection) => { directory.set(state({ current: selection })) @@ -69,7 +74,8 @@ describe('ModelSelect reasoning effort', () => { fireEvent.click(trigger) fireEvent.click(screen.getByRole('menuitem', { name: /推理等级/ })) expect(screen.getAllByRole('menuitemradio').map(item => item.textContent)) - .toEqual(['Off', 'High', 'MaxLargest budget']) + .toEqual(['Off', 'High', 'Max']) + expect(screen.queryByText('Largest budget')).toBeNull() fireEvent.click(screen.getByRole('menuitemradio', { name: /Max/ })) await waitFor(() => { @@ -133,6 +139,7 @@ describe('ModelSelect reasoning effort', () => { fireEvent.click(screen.getByRole('menuitem', { name: /模型/ })) expect(screen.queryByRole('menuitemradio', { name: 'removed-model' })).toBeNull() expect(screen.getByRole('menuitemradio', { name: 'DeepSeek-V4-Flash' })).toBeTruthy() + expect(screen.queryByText('Fast catalog description')).toBeNull() }) it('shows loading until the catalog and Session projection are both ready', async () => {