From f3de75ce1a1b30abecda7f4e520ee19128fd83f4 Mon Sep 17 00:00:00 2001 From: xiaofei Date: Sun, 1 Nov 2026 21:33:20 -0800 Subject: [PATCH] =?UTF-8?q?fix(settings):=20Anthropic=20=E5=85=BC=E5=AE=B9?= =?UTF-8?q?=E4=BE=9B=E5=BA=94=E5=95=86=E7=9A=84=E6=A8=A1=E5=9E=8B=E5=8F=AF?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E8=BE=93=E5=85=A5=E8=83=BD=E5=8A=9B=20(#1045?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit K-brain 按模型条目的 inputModalities/vision 判定能否收图,与 API 类型无关; 前端推送、modelFactory 与模型选择器也都不区分供应商。唯一缺口是设置页的 providerSupportsModelInputModalitiesOverride 把 claude_code 排除在外, Anthropic 兼容供应商因此无法开启图片输入。放开该门控,并补推送/读回测试。 --- .../providers/kbrain-attachment-read.test.mjs | 27 +++++++++++++++++++ .../test/settings/input-modalities.test.mjs | 7 ++--- .../src/pages/settings/providerUtils.ts | 5 +++- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/crates/agent-gui/test/providers/kbrain-attachment-read.test.mjs b/crates/agent-gui/test/providers/kbrain-attachment-read.test.mjs index 1d3711127..028ddc9d6 100644 --- a/crates/agent-gui/test/providers/kbrain-attachment-read.test.mjs +++ b/crates/agent-gui/test/providers/kbrain-attachment-read.test.mjs @@ -4,6 +4,7 @@ import { createTsModuleLoader } from "../helpers/load-ts-module.mjs"; const loader = createTsModuleLoader(); const { + customProviderFromKBrain, kBrainSettingsUpdateForMissingInputModalities, kBrainSettingsUpdateFromLegacyProviders, } = loader.loadModule( @@ -48,6 +49,32 @@ test("relay models without modalities are pushed to K-brain with catalog vision" assert.equal(pushedModel(update, "relay", "unknown-relay-model").inputModalities, undefined); }); +test("anthropic-compatible user modality override reaches K-brain and survives reload", () => { + // #1045:目录未知的中转模型,用户在设置里选「文本与图片」后必须原样推给 K-brain + // (K-brain 只按 inputModalities/vision 判定收图),再读回时仍保留该覆盖。 + const saved = relayProvider("relay", [ + { id: "unknown-relay-model", contextWindow: 8192, inputModalities: ["text", "image"] }, + ]); + const update = kBrainSettingsUpdateFromLegacyProviders([saved], [saved]); + const pushedProvider = update.providers.find((provider) => provider.id === "relay"); + assert.equal(pushedProvider.api, "anthropic-messages"); + assert.deepEqual(pushedModel(update, "relay", "unknown-relay-model").inputModalities, [ + "text", + "image", + ]); + + const reloaded = customProviderFromKBrain({ + id: "relay", + name: "relay", + api: "anthropic-messages", + baseUrl: "https://relay.invalid/claude/v1", + apiKeyConfigured: true, + models: [{ id: "unknown-relay-model", contextWindow: 8192, vision: true }], + }); + assert.equal(reloaded.type, "claude_code"); + assert.deepEqual(reloaded.models[0].inputModalities, ["text", "image"]); +}); + test("explicit modalities win and are shared across providers for the same model id", () => { const explicit = relayProvider("a", [{ id: "claude-opus-4-6", inputModalities: ["text"] }]); const implicit = relayProvider("b", [{ id: "claude-opus-4-6" }]); diff --git a/crates/agent-gui/test/settings/input-modalities.test.mjs b/crates/agent-gui/test/settings/input-modalities.test.mjs index 157f53d7d..50c1c9f8d 100644 --- a/crates/agent-gui/test/settings/input-modalities.test.mjs +++ b/crates/agent-gui/test/settings/input-modalities.test.mjs @@ -5,8 +5,8 @@ import { createTsModuleLoader } from "../helpers/load-ts-module.mjs"; // inputModalities(模型输入模态用户覆盖)的反漂移锁: // 1. normalizer 的过滤/补齐/规范顺序契约; // 2. 设置加载往返不丢字段; -// 3. modelFactory 只在附件发送确实受 model.input 门控的分支(codex/gemini/ -// deepseek)应用覆盖,anthropic 不适用(避免虚假能力声明)。 +// 3. modelFactory 对所有供应商(含 Anthropic 兼容)应用覆盖:K-brain 按模型条目的 +// inputModalities 判定能否收图,与 API 类型无关(#1045)。 const loader = createTsModuleLoader(); const { normalizeInputModalities, normalizeProviderModelConfig, normalizeProviderModelConfigs } = loader.loadModule("src/lib/settings/index.ts"); @@ -106,7 +106,8 @@ test( assert.equal(providerSupportsModelInputModalitiesOverride("xai"), true); assert.equal(providerSupportsModelInputModalitiesOverride("gemini"), true); assert.equal(providerSupportsModelInputModalitiesOverride("deepseek"), true); - assert.equal(providerSupportsModelInputModalitiesOverride("claude_code"), false); + // #1045:Anthropic 兼容供应商同样可以手动开启图片输入。 + assert.equal(providerSupportsModelInputModalitiesOverride("claude_code"), true); }, ); diff --git a/crates/agent-ui/src/pages/settings/providerUtils.ts b/crates/agent-ui/src/pages/settings/providerUtils.ts index ec3a48f82..9c81a77c1 100644 --- a/crates/agent-ui/src/pages/settings/providerUtils.ts +++ b/crates/agent-ui/src/pages/settings/providerUtils.ts @@ -42,7 +42,10 @@ export function providerSupportsModelInputModalitiesOverride(providerId: Provide // deepseek:Responses wire 已接受 input_image(官方《图像理解》指南),模型 // 能力默认按 id 推断(flash 家族吃图、Pro 纯文本),中转端点不吃图时用覆盖 // 改回 ["text"]。 - providerId === "deepseek" + providerId === "deepseek" || + // claude_code(Anthropic 兼容):K-brain 按模型条目的 inputModalities 判定能否 + // 收图,与 API 类型无关;中转的 /v1/models 不返回模态时,靠覆盖开启图片输入(#1045)。 + providerId === "claude_code" ); }