diff --git a/.agents/notes/implemented/feature/2026-09-14-model-image-input-settings.i18n.yaml b/.agents/notes/implemented/feature/2026-09-14-model-image-input-settings.i18n.yaml index dc1712100e..a0663094ac 100644 --- a/.agents/notes/implemented/feature/2026-09-14-model-image-input-settings.i18n.yaml +++ b/.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 diff --git a/.agents/notes/implemented/feature/2026-09-14-model-image-input-settings.md b/.agents/notes/implemented/feature/2026-09-14-model-image-input-settings.md index b418fe4649..ef32840033 100644 --- a/.agents/notes/implemented/feature/2026-09-14-model-image-input-settings.md +++ b/.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. diff --git a/.agents/notes/implemented/feature/2026-09-14-model-image-input-settings.zh.md b/.agents/notes/implemented/feature/2026-09-14-model-image-input-settings.zh.md index 0cc777bc9b..70ec427b13 100644 --- a/.agents/notes/implemented/feature/2026-09-14-model-image-input-settings.zh.md +++ b/.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 图片时保留图片限制。** 这会留下适配器拒绝保存的配置。清除图片专属限制,使选择的仅文本状态有效,同时保留无关模型字段。 diff --git a/apps/web/tests/onboarding-deepseek-config.e2e.ts b/apps/web/tests/onboarding-deepseek-config.e2e.ts index 4f2132615c..8906445d01 100644 --- a/apps/web/tests/onboarding-deepseek-config.e2e.ts +++ b/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++) { diff --git a/packages/client/ui-settings-models/README.i18n.yaml b/packages/client/ui-settings-models/README.i18n.yaml index 577ff6e604..8db4fd6c6d 100644 --- a/packages/client/ui-settings-models/README.i18n.yaml +++ b/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 diff --git a/packages/client/ui-settings-models/README.md b/packages/client/ui-settings-models/README.md index ac6b33a4d9..a26cd22f87 100644 --- a/packages/client/ui-settings-models/README.md +++ b/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 diff --git a/packages/client/ui-settings-models/README.zh.md b/packages/client/ui-settings-models/README.zh.md index 5f780520ad..82b9e031b4 100644 --- a/packages/client/ui-settings-models/README.zh.md +++ b/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`,因为适配器在没有图片输入时拒绝这些限制。仅为实际能够处理图片的模型声明支持。 ### 新增与删除提供方 diff --git a/packages/client/ui-settings-models/src/client/ModelListEditor.tsx b/packages/client/ui-settings-models/src/client/ModelListEditor.tsx index a8908d19f7..1f7cb64698 100644 --- a/packages/client/ui-settings-models/src/client/ModelListEditor.tsx +++ b/packages/client/ui-settings-models/src/client/ModelListEditor.tsx @@ -164,8 +164,6 @@ export function ModelListEditor(props: ModelListEditorProps): ReactNode { const [candidates, setCandidates] = useState(undefined) const [picked, setPicked] = useState>(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>(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 diff --git a/packages/client/ui-settings-models/src/client/ModelsSection.module.css b/packages/client/ui-settings-models/src/client/ModelsSection.module.css index 2dc72373ea..916e0f9b49 100644 --- a/packages/client/ui-settings-models/src/client/ModelsSection.module.css +++ b/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. */