DeepSeek Harness 用 opencode-go 里的 deepseek-v4.1-flash 模型,怎么启用视觉输入喵
事情是这样的喵 —— 本喵想让 DSH 帮忙看看远程机器上的程序界面,结果
read_image 一爪子下去就炸了:
cannot read "xxx.png" as an image: model "deepseek-v4.1-flash" does not declare
image input; switch to an image-capable model to read images
为什么会走到自建 provider 这条路喵
起因是官方的 opencode-go 里模型列表是锁死的呀,本喵想把
deepseek-flash / deepseek-v4.1-flash 加进去,它不让加喵。
那没办法,只能自己建一个 provider(就叫它 opencode-custom 好了),
把想要的模型塞进去:
opencode-custom:
apiKeyEnv: OPENCODE_NEW_API_KEY
api: openai-responses
baseURL: https://opencode.ai/zen/go/v1
models:
- id: deepseek-v4.1-flash # 加进来了喵!
诶,能用了诶!……但是没有视觉喵。(尾巴垂下来)
呜……明明走的是同一个网关,同一个多模态模型,怎么就不认图了呢?
不是模型的问题喵
第一反应当然是「这个模型是不是不支持图片」。但是不对呀喵!
同一个 baseURL、同一个 api: openai-responses 协议,网关本来就是多模态的。
所以问题不在模型,也不在网关 —— 是 DSH 的模型元数据里没写「本喵看得见」。
那就只能去翻源码了喵。(磨爪子)
把 app.asar 扒开看看喵
DSH 桌面版是 Electron 做的,代码都塞在:
C:\Users\exat500g\AppData\Local\Programs\DSH Desktop\resources\app.asar
asar 是「文件头 + 拼接内容」的归档格式喵,里面的文本是明文,不用解包, 当纯文本搜就行啦:
$app = "C:\Users\exat500g\AppData\Local\Programs\DSH Desktop\resources\app.asar"
Select-String -Path $app -Pattern "declare image input" -Encoding UTF8
一把就中了喵!检查代码长这样:
if (info.inputModalities === undefined || !info.inputModalities.includes("image"))
throw new Error(
`cannot read "${requestedPath}" as an image: model "${model}" does not ` +
`declare image input; switch to an image-capable model to read images`
);
也就是说,只要 info.inputModalities 里面有 "image" 就好啦喵~
顺着 inputModalities 往上爬喵
Select-String -Path $app -Pattern "inputModalities" -Encoding UTF8
59 处命中呢,好多喵……挑关键的看,一路爬到 pi-ai 的 adapter:
modelInfo(snapshot, provider, model) {
const profile = this.profileOf(snapshot, provider);
const resolvedModel = this.modelOf(snapshot, provider, model);
return {
provider,
id: model,
name: resolvedModel.name,
inputModalities: [...resolvedModel.input], // ← 源头在这里喵
context: { contextWindow: resolvedModel.contextWindow },
...
};
}
inputModalities 完全是配置里模型的 input 字段变出来的喵。
再看这个字段的定义:
name?: string;
reasoning?: boolean;
input?: ("text" | "image")[];
cost?: { input: number; output: number; cacheRead: number; cacheWrite: number };
contextWindow?: number;
maxTokens?: number;
诶诶诶?默认值本来就是对的喵!
继续往下搜默认值,catalog 那层是这么写的:
const models = modelDefinitions.map((definition) => ({
id: definition.id,
name: definition.name ?? definition.id,
reasoning: definition.reasoning ?? false,
input: definition.input ?? ["text", "image"], // ← 默认就含 image 喵!
contextWindow: definition.contextWindow ?? 128000,
maxTokens: definition.maxTokens ?? 16384,
}));
pi-ai 这层默认就是 ["text", "image"] 呀!
所以根本不是「模型不支持」,而是 DSH 的设置层把没写 input 的模型
收敛成了 ["text"],把好好的默认值盖掉了喵……
那为什么自建 provider 会漏掉这个字段喵?
因为 DSH 的「模型设置」页面只让编辑四个字段喵:
每个 DeepSeek 行编辑
id、可选显示name与可选contextWindow/maxTokens
input 不在里面呀! 所以不管是 GUI 建的自定义 provider,还是手工塞进去
的模型行,都只会写这四个字段 —— input 就永远缺席,视觉就永远开不起来喵。
(好消息是同一段原文还有一句:「该精选集之外的现有字段在编辑后仍会保留」,
所以手工加上去的 input 不会被 GUI 冲掉,可以放心继续用 GUI 改容量~)
改一行就好啦喵
C:\Users\exat500g\.dsh\settings.yaml,在每个想开视觉的模型下面加一行:
llm-pi-ai:
providers:
opencode-custom:
apiKeyEnv: OPENCODE_NEW_API_KEY
api: openai-responses
baseURL: https://opencode.ai/zen/go/v1
models:
- id: deepseek-v4.1-flash
input: ["text", "image"] # ← 加这一行喵
原来那个 opencode-new 底下也一样加:
- id: deepseek-flash
contextWindow: 1000000
maxTokens: 384000
input: ["text", "image"] # ← 这个本喵之前加的
- id: deepseek-v4.1-flash
input: ["text", "image"] # ← 后来补上的
- id: qwen3.8-flash # ← 没加,还是纯文本喵
热生效哦,不用重启的喵~ 改完立刻再读一次图片,成了!
保险起见先用 python -c "import yaml; ..." 过一下语法喵,
别把 settings 写坏了,写坏了本喵会哭的呜呜。
三个坑,要记住喵
1. 要一个一个模型地加
input 是按模型声明的,不是按 provider。同一个 provider 下面的
qwen3.8-flash 没写,它就还是只有文本喵。别想着加一次全开哦~
2. 打开视觉 ≠ 模型真的看得见喵
这个改动只是让 DSH 愿意把图片发出去而已。模型和网关那边到底认不认, 是另一回事喵 —— 如果调用报 400 之类的错,那就是真的不认,把字段去掉吧。
3. GUI 不会帮你加,也不会帮你删
「模型设置」页面加不了 input(只露四个字段),但也不会把已有的
input 抹掉。所以第一次得手工加,之后随便用 GUI 改容量都安全喵。
附:图片会被缩小喵
实测 3450x2880 的截图送进模型之前会被缩到 2241x1871,还会附一句提示:
(downscaled from 3450x2880 px; multiply coordinates by 1.54 to locate features
in the original file)
如果要按屏幕坐标去点击,记得乘这个系数喵,不然就点歪了, 点到别的地方去了喵……(捂脸)
小结喵
- 官方
opencode-go不让加模型 → 自建 provider 是没办法的办法,没问题喵 - 但 GUI 建出来的模型条目只有四个字段,没有
input inputModalities就来自input;pi-ai 层默认本来是["text","image"], 被设置层收敛成了["text"]- 手工加一行
input: ["text", "image"]就好啦,热生效
至于怎么找到的嘛 —— asar 是明文的,别怕它,直接搜报错信息就行了喵! 从报错字符串出发,一路顺着标识符往上爬,比翻文档快多了喵~
(本喵去玩了,主人记得备份 settings 呀!)