dsh(0.1.5-rc.1)接入官方模型deepseek-flash图像输入不支持修复

DSH 官方模型图像输入"不支持"问题排查与修复

摘要 :dsh 0.1.5-rc.1 中,手写进 settings.yaml 的官方模型条目默认是纯文本的,缺少 inputModalities: [text, image] 声明会导致发图时被 harness 在请求发出前直接拒绝(UNSUPPORTED_CONTENT)。根因是 llm-deepseek.models 一旦出现即整体替换内置目录(非按 id 合并),手写条目未声明该字段时 schema 缺省为 ["text"]。修复只需在模型条目中补一行 inputModalities: [text, image],无需装插件、换模型或重启;实测 headless 冷启动端到端验证通过。文章还梳理了三个常见误区(字段名写错、网上插件方案不可靠、models 列表是整体替换而非补充)及 catalog 字段参考表。

文章目录

  • [DSH 官方模型图像输入"不支持"问题排查与修复](#DSH 官方模型图像输入"不支持"问题排查与修复)

环境:dsh 0.1.5-rc.1(npm 全局安装,~/.local 前缀)· Ubuntu · 官方路由 deepseek-official / deepseek-flash

修复方案

  • dsh(DeepSeek Harness)中手写进配置的模型条目默认是纯文本的 。官方路由 llm-deepseek 的模型目录(catalog)条目如果不声明 inputModalities: [text, image],发图时 harness 会在请求发出前直接拒绝(UNSUPPORTED_CONTENT)。在 ~/.dsh/settings.yaml 的模型条目里补一行即可修复:
yaml 复制代码
llm-deepseek:
  models:
    - id: deepseek-flash
      name: deepseek-flash
      contextWindow: 1000000
      inputModalities: [text, image]   # ← 关键就是这一行
  • 不需要装任何插件,不需要换模型,不需要重启(官方文档:模型变更下次请求即生效;实测 headless 冷启动验证通过)。

现象

  • 把 dsh 从 0.1.2 升级到 0.1.5-rc.1 后,Web UI 中通过官方渠道(agent-default-model: provider: deepseek-official, model: deepseek-flash)发送图片,提示"不支持"。同一个 deepseek-flash 模型本身是原生多模态的(DeepSeek-V4.1-Flash),问题显然出在 harness 侧。
    当时配置长这样:
yaml 复制代码
llm-deepseek:
  models:
    - id: deepseek-flash
      name: deepseek-flash
      contextWindow: 1000000
  • 而同一个 settings.yaml 里,自定义 provider(llm-pi-ai.providers.new-api)下的模型都声明了 input: [text, image] 且能正常发图。

排查过程

1. 排除配置文件语法问题

  • dsh --profile web --dump-config 一直报 failed to parse: value expected,一度怀疑 settings.yaml 被运行时写坏。用 Python YAML 逐个校验 settings.yamlcordis.patch.yml 均合法。最后发现是乌龙--dump-config 输出的是 YAML 而不是 JSON,之前用 jq 去解析才报的错。输出里还带 !!js dshHomePath(...) 这类自定义 tag,是 cordis 的运行时表达式,属于正常现象。

教训:先看命令的原始输出和退出码,再怀疑配置本身。

2. dump 看不到模型目录

  • --dump-config 输出的是插件注册树(patch layers),llm-deepseek 插件没有 config 块,模型目录不在这里。真正的逻辑在依赖包里:
bash 复制代码
node_modules/@deepseek-ai/dsh/node_modules/@deepseek-ai/dsh-llm-deepseek/lib/index.js

3. 读源码,三个关键证据

  • 证据一:拒绝发生在 harness 内部,读的是 inputModalities
js 复制代码
// L1620 --- 发图前的门禁
if (connection.models.find((entry) => entry.id === options.model)
      ?.inputModalities?.includes("image") !== true)
  throw new LlmError(`DeepSeek model "${options.model}" does not accept image input.`, "UNSUPPORTED_CONTENT");

证据二:catalog 条目的 schema,inputModalities 缺省值是纯文本

js 复制代码
// L1877-1879 --- catalogModel zod schema(节选)
contextWindow: z.number().step(1).min(1),
maxTokens: z.number().step(1).min(1),
inputModalities: z.array(z.union(MODEL_MODALITIES)).min(1).default(["text"]),
//                                              MODEL_MODALITIES = ["text", "image"]

证据三:插件自带的默认目录本来就有图像支持,但 models 是整体覆盖不是合并

js 复制代码
// L1843-1846 --- DEFAULT_MODELS(节选)
{
  id: "deepseek-flash",
  name: "DeepSeek-V41-Flash",
  contextWindow: DEFAULT_CONTEXT_WINDOW,        // 1e6
  inputModalities: ["text", "image"],
  imagePixelBudget: DEFAULT_REQUEST_IMAGE_PIXEL_BUDGET,  // 64e4
  imageMaxBytes: DEFAULT_REQUEST_IMAGE_MAX_BYTES,        // 1 MiB
  systemPromptUpdate: "in-history"
},
js 复制代码
// L1896 --- models 缺省用 DEFAULT_MODELS;一旦手写,整个目录被替换
models: z.array(catalogModel).default(DEFAULT_MODELS),

根因

  • llm-deepseek.models 一旦出现在 settings.yaml,整个内置目录被这条列表替换 (不是按 id 合并)。手写的 deepseek-flash 条目没有 inputModalities,schema 缺省填 ["text"],于是 L1620 的门禁在发图前直接拒绝,模型本身的能力根本没被问到。
  • 另外两个字段其实都是冗余的:contextWindow: 1000000 等于默认值 DEFAULT_CONTEXT_WINDOW = 1e6name 只影响显示。真正的信息损失只有 inputModalities(连带 imagePixelBudget/imageMaxBytes,不过这两个在图像模型上不声明时会回退到同一组默认值,行为等价)。

修复

  • 按惯例先备份(~/.dsh/ 里的 .bak-* 命名传统),再做最小增量修改:
bash 复制代码
cp -p ~/.dsh/settings.yaml ~/.dsh/settings.yaml.bak-before-image-input
  • 在条目中加一行 inputModalities: [text, image]。字段顺序无关,其余段(ui-onboarding、pet、live-stats 等运行时状态)完全不动。

验证

  • 静态 :YAML 解析通过;dsh --profile web --dump-config 退出码 0。
  • 端到端 :用 headless profile 做一次性任务,走的正是官方 deepseek-official/deepseek-flash 路由:
bash 复制代码
python3 -c "from PIL import Image; Image.new('RGB',(16,16),(255,0,0)).save('/tmp/smoke.png')"
dsh --profile headless "Use the file read tool to view /tmp/smoke.png, then reply with only the dominant color."
# → Red
  • 图像经工具链进入模型消息、通过门禁、被正确识别。修复前这条路径会抛 does not accept image input
  • 关于运行中的 web 实例 :官方文档说明模型变更下次请求生效、无需重启。如果 Web UI 仍提示不支持(实例启动于修改前),重启 dsh web 即可。

三个常见误区(都是踩过的坑或差点踩的坑)

  1. 字段名写成 inputinput: [text, image]llm-pi-ai 自定义 provider 的写法;官方路由 llm-deepseek 用的是 inputModalities。两者 schema 不同源,写错了不会被读取(问题原样保留),静默失败最迷惑人。
  2. 网上搜到的插件方案 。实测 dsh-paste-input 在 npm 上根本不存在(404);dsh-attach-picker 存在但没有必要------上传/粘贴/拖拽链路是内置的,卡点只在模型声明。搜到"装插件 + 硬刷新"之类方案时,先确认包名是否真实存在。
  3. 以为 models 列表是"补充" 。它是整体替换。手写一条 deepseek-flash 会把内置的 deepseek-v4-flashdeepseek-v4-prodeepseek-v4-flash-vision-exp 全部挤掉。如果只想微调官方模型,替换列表时要自己把需要的能力字段带全;如果没有任何特殊需求,直接删掉 models 覆盖、用内置目录是最稳的。

附:llm-deepseek catalog 模型条目字段参考

来自 catalogModel schema 与 resolveModels 校验逻辑:

字段 类型 缺省 说明
id string 必填 非空;列表内不可重复
name string 显示名(内置 flash 为 "DeepSeek-V41-Flash")
contextWindow 正整数 1000000 上下文窗口
maxTokens 正整数 内置默认 单次最大输出
inputModalities ["text"] / ["text","image"] ["text"] 图像输入开关;非空、无重复、仅许 text/image
imagePixelBudget "low" 或正整数 640000 单请求像素预算;仅图像模型可声明
imageMaxBytes 正整数 1048576 单请求图像字节上限;仅图像模型可声明
systemPromptUpdate "in-history" 目前唯一合法值

校验细节:纯文本模型声明图像限额会直接报错;imageDetail 字段已废弃(改用 imagePixelBudget)。

相关推荐
lovingsoft2 小时前
AI测试中的对抗测试:从“找Bug”到“预演战争”
人工智能·bug
golang学习记1 天前
VS Code 新UI被曝重大bug:菜单栏消失了
vscode·bug
j7~1 天前
【软件测试】(概念篇)《需求的概念,测试用例(case),软件错误(bug),开发模型以及测试模型》---详解
软件测试·bug·敏捷模型·开发模型·测试模型·测试中需求的定义·软件的生命周期
冰水不凉2 天前
CodeX 在 Ubuntu 下 vscode 插件的 bug 问题
vscode·ubuntu·bug
flower_drop3 天前
基于 WebUSB 与 CDP:在浏览器端实现 Android 设备通信与无证书抓包实践
android·chrome·测试工具·adb·bug·edge浏览器
lytao1233 天前
90% 覆盖率不等于没 Bug:用风险配置测试组合
前端·javascript·bug·软件工程
Joker-Full-stack3 天前
higress Grafana生产事故复盘:Loki 2.9 WAL 竞态 Bug 导致 5 天审计日志消失
bug·grafana·higress
我星期八休息6 天前
软件测试—从认识到BUG
考研·安全·bug