Просмотр исходного кода

fix(web): address model image settings review

kingwl 2 недель назад
Родитель
Сommit
41e591b3e4

+ 2 - 2
.agents/notes/implemented/feature/2026-09-14-model-image-input-settings.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 .agents/notes/implemented/feature/2026-09-14-model-image-input-settings.md
-2026-09-14-model-image-input-settings.md: b418fe46496aa6ed40b427e30f5340b24a6b8da0
-2026-09-14-model-image-input-settings.zh.md: 0cc777bc9b8f942184524c9c6c0774acd9642956
+2026-09-14-model-image-input-settings.md: ef328400332aa58c02a450ead0195eff124c5fdf
+2026-09-14-model-image-input-settings.zh.md: 70ec427b138026124cad2ffdb1fc5f60597abe8a

+ 2 - 0
.agents/notes/implemented/feature/2026-09-14-model-image-input-settings.md

@@ -16,6 +16,8 @@ The shared field replaces one drafted row and preserves unrelated metadata. Sele
 
 ## Alternatives considered
 
+**Keep `input` editable only in the settings document.** The [earlier pi-ai modality decision](../../archived/architecture/2026-08-12-pi-ai-route-default-input-modalities.md) kept this field outside the model-list editor. That leaves users who add custom vision models through the UI unable to enable their image input there. Per-row editing supplies that configuration while the default choice preserves catalog inheritance.
+
 **A two-state switch.** Treating an absent pi-ai declaration as disabled would misrepresent inherited vision support and encourage overwriting catalog defaults. The explicit Default choice preserves the adapter's existing resolution rules.
 
 **Keep image limits when disabling DeepSeek images.** This leaves a configuration that the adapter refuses to save. Clearing the image-specific limits makes the selected text-only state valid while preserving unrelated model fields.

+ 2 - 0
.agents/notes/implemented/feature/2026-09-14-model-image-input-settings.zh.md

@@ -16,6 +16,8 @@ Status: implemented
 
 ## 考虑过的替代方案
 
+**仅允许在设置文档中编辑 `input`。** [早期 pi-ai 输入模态决策](../../archived/architecture/2026-08-12-pi-ai-route-default-input-modalities.md)将该字段留在模型列表编辑器之外。这使通过 UI 添加自定义视觉模型的用户无法在同一界面启用图片输入。逐行编辑提供了该配置,而默认选项保留模型目录继承。
+
 **两态开关。** 将缺省的 pi-ai 声明视为禁用,会错误表达继承的视觉能力,并促使用户覆盖模型目录的默认值。显式「默认」选项保留适配器现有的解析规则。
 
 **禁用 DeepSeek 图片时保留图片限制。** 这会留下适配器拒绝保存的配置。清除图片专属限制,使选择的仅文本状态有效,同时保留无关模型字段。

+ 3 - 1
apps/web/tests/onboarding-deepseek-config.e2e.ts

@@ -221,11 +221,13 @@ describe.skipIf(MODE === 'record')('web e2e: first-run DeepSeek credential setup
     expect(savedDefaults).toContain('id: deepseek-flash')
     expect(savedDefaults).toContain('inputModalities:')
     expect(savedDefaults).toContain('- text')
-    expect(savedDefaults).toContain('- image')
     expect(savedDefaults).toContain('systemPromptUpdate: in-history')
     await expect(scaffold.ctx.llm.resolveModelInfo('deepseek-official', 'deepseek-flash')).resolves.toMatchObject({
       name: 'Configured Flash', inputModalities: ['text'], systemPromptUpdate: 'in-history',
     })
+    await expect(scaffold.ctx.llm.resolveModelInfo('deepseek-official', 'deepseek-v4-flash-vision-exp')).resolves.toMatchObject({
+      inputModalities: ['text', 'image'],
+    })
     await deepSeek.locator('xpath=ancestor::li').getByRole('button', { name: '编辑' }).click()
     await settings.getByText('自定义设置').click()
     for (let index = 0; index < 4; index++) {

+ 2 - 2
packages/client/ui-settings-models/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-settings-models/README.md
-README.md: ac6b33a4d90eeb0164c1c0afb19ff57ca24775a6
-README.zh.md: 5f780520adeaa4af0f133484af4ed9c377890c5d
+README.md: a26cd22f8785f8f2c948ac74560ff85d80097577
+README.zh.md: 82b9e031b477aa83c59acdfbade38f8282adefbd

+ 1 - 1
packages/client/ui-settings-models/README.md

@@ -39,7 +39,7 @@ The collapsed 自定义设置 fold carries the curated extras: `baseURL` for bot
 
 The DeepSeek card edits the shared `llm-deepseek` endpoint, credentials, and model catalog without a protocol selector. When Cordis YAML selects Messages, the public endpoint placeholder is `https://api.deepseek.com/anthropic`. Saving the card preserves protocol configuration.
 
-Expand **Customized settings → Model options → Image input** to declare whether the model supports images. **Supported** writes text and image input; **Not supported** writes text only. **Default** removes the explicit declaration: DeepSeek defaults to text only, while pi-ai inherits the installed model catalog or provider default. DeepSeek stores this choice in `inputModalities`; pi-ai stores it in `input`. Selecting text only or default for DeepSeek also removes `imagePixelBudget` and `imageMaxBytes`, which its adapter rejects without image input. Declare support only for models that can actually process images.
+Expand **Customized settings → Model options → Image input** to declare whether the model supports images. **Supported** writes text and image input; **Not supported** writes text only. DeepSeek's **Default (text only)** removes `inputModalities`; an absent declaration means text only. Pi-ai's **Use default** removes `input` and inherits the installed model catalog or provider default. Selecting **Not supported** or **Default (text only)** for DeepSeek also removes `imagePixelBudget` and `imageMaxBytes`, which its adapter rejects without image input. Declare support only for models that can actually process images.
 
 ### Adding and deleting providers
 

+ 1 - 1
packages/client/ui-settings-models/README.zh.md

@@ -39,7 +39,7 @@ kind: "package-reference"
 
 `llm-deepseek` 的 DeepSeek 卡片编辑共用的端点、凭据和模型目录,不提供协议选择器。Cordis YAML 选择 Messages 时,官方端点占位符为 `https://api.deepseek.com/anthropic`;保存卡片不会改写协议配置。
 
-展开**自定义设置 → 模型选项 → 图片输入**,声明模型是否支持图片。选择**支持**会写入文本和图片输入;选择**不支持**会写入仅文本。选择**默认**会移除显式声明:DeepSeek 默认为仅文本,pi-ai 则继承已安装模型目录或提供方的默认值。DeepSeek 将此选项存入 `inputModalities`,pi-ai 存入 `input`。DeepSeek 切为仅文本或默认时,还会移除 `imagePixelBudget` 和 `imageMaxBytes`,因为适配器在没有图片输入时拒绝这些限制。仅为实际能够处理图片的模型声明支持。
+展开**自定义设置 → 模型选项 → 图片输入**,声明模型是否支持图片。选择**支持**会写入文本和图片输入;选择**不支持**会写入仅文本。DeepSeek 的**默认(仅文本)**会移除 `inputModalities`;缺省的声明表示仅文本。Pi-ai 的**使用默认值**会移除 `input`,继承已安装模型目录或提供方的默认值。DeepSeek 选择**不支持**或**默认(仅文本)**时,还会移除 `imagePixelBudget` 和 `imageMaxBytes`,因为适配器在没有图片输入时拒绝这些限制。仅为实际能够处理图片的模型声明支持。
 
 ### 新增与删除提供方
 

+ 0 - 2
packages/client/ui-settings-models/src/client/ModelListEditor.tsx

@@ -164,8 +164,6 @@ export function ModelListEditor(props: ModelListEditorProps): ReactNode {
   const [candidates, setCandidates] = useState<readonly LlmDiscoveredModel[] | undefined>(undefined)
   const [picked, setPicked] = useState<ReadonlySet<string>>(new Set())
   const [candidateQuery, setCandidateQuery] = useState('')
-  // Rows carry an id and a name; capacities are the exception, so they stay
-  // folded until asked for rather than crowding every row with four inputs.
   const [expanded, setExpanded] = useState<ReadonlySet<number>>(new Set())
   // Capacities are edited as text, so a field's keystrokes are held here rather
   // than re-derived from the parsed count on every change — that would rewrite

+ 2 - 2
packages/client/ui-settings-models/src/client/ModelsSection.module.css

@@ -432,8 +432,8 @@
 }
 
 /* Model list, shared with the pi-ai provider form: one bordered
-   entry per model, id and display name on the row, capacities behind the
-   row's own disclosure. The rules use this stylesheet's token vocabulary —
+   entry per model, id and display name on the row, capacities and image input
+   behind the row's own disclosure. The rules use this stylesheet's token vocabulary —
    `--dsw-alias-border-subtle`, `--dsw-alias-text-tertiary`, and
    `--dsw-alias-text-primary` are undefined here and would resolve to their
    light-mode literals. */