Skip to content

[ac#1420] 自带 key 的模型如实标注能否看图 —— 直连按 models.dev、平台一律不能、自定义只认用户声明 - #1437

Merged
jinjunnn merged 3 commits into
alphafrom
feat/1420-byok-image-capability
Sep 24, 2026
Merged

jinjunnn merged 3 commits into
alphafrom
feat/1420-byok-image-capability

Conversation

@jinjunnn

@jinjunnn jinjunnn commented Sep 24, 2026

Copy link
Copy Markdown
Owner

Fixes #1420
Refs jinjunnn/alpha-work#105

这次改了什么

自带 Key 的模型里本来就能看图的几个(Qwen3.8 Max、Kimi K3、Kimi K2.6),用户发给它们的图片现在会原样送到模型。以前 Code Puppy 在发出之前就把图片换成了一句 “this model does not support image input”。平台代理模型保持原样,仍然看不了图。自己填地址接入的服务,只有用户显式声明过的模型才算能看图。

这个值由谁写、由谁读

  • 谁读:引擎的 capabilities.input.image,由 packages/opencode/src/provider/transform.tsunsupportedParts() 读取(if (model.capabilities.input[modality]) return part)。[REQ-228][CODE] 看不了图的模型:贴图和读到的图片自动送云端识图,模型可按路径追问 #1419 的识图插件以后也读这个值。
  • 谁写:引擎 provider.ts 的 config 合并,公式是 model.modalities?.input?.includes("image") ?? existingModel?.capabilities.input.image ?? false注入的 config 里没写 modalities 时,它会回落到 models.dev 底表里 provider id 和 model id 都相同的那一条。
  • 改之前alpha-models.ts 三段注入都不写 modalities,所以全部模型都判成 false。它不是逻辑 bug,是数据模型里缺字段:直连目录和自定义节点记录都没有地方写“能不能看图”。

数据从哪里来(勘破结论)

  • 打包后的 sidecar 在运行时拿不到 models.dev 数据。 托管模式下,OPENCODE_MODELS_PATH 指向 alpha 自己物化的最小底表(alpha-config-injection.ts governedModelsBase,所有模型都写死 modalities.input: [])。ui-mac 的构建也没有 OPENCODE_MODELS_DEV 的 define。所以“查 models.dev 快照”只能在编目录时做,运行时做不了。

  • 直连目录alpha-models.json):每个 provider 新增两个字段。

    • modelsDev:出处,也就是和本条 baseURL 同一个端点的那个 models.dev provider id。
    • imageInput:逐个模型显式写 true 或 false。值来自 2026-09-23 的 https://models.dev/api.json(sha256 219bfd29…),查同名模型的 modalities.input 里有没有 image
    provider(modelsDev) 能看图 不能看图
    deepseek(deepseek deepseek-flash(按实测覆盖 models.dev,见下), deepseek-v4-pro
    zhipuai(zhipuai glm-5.2, glm-4.5-air
    minimax(minimax-cn MiniMax-M2
    alibaba(alibaba-cn qwen3.8-max qwen-plus, qwen3-coder-plus
    moonshot(moonshotai-cn kimi-k3, kimi-k2.6

    alibaba 和 moonshot 的国际站 id(alibabamoonshotai)给出的值相同。

    唯一例外:deepseek-flash 取 false(与 models.dev 不一致,按实测证据取 false,直连实测后再定)。 models.dev 把它标成能看图;协调方 2026-09-24 实测:OpenRouter 目录里 deepseek/deepseek-v4-flashinput_modalities["text"],带图请求(含 provider.only=["deepseek"] 官方端点)返回 404 No endpoints found that support image input(15 个端点全部不支持图片),能看图的是另一个型号 deepseek-v4-flash-vision-exp。我们没有 DeepSeek 直连 key,没法实测 deepseek-flash 这个直连 id,所以按 fail-closed 取 false:错标 false 的代价是多一次云端识图,错标 true 的代价是用户贴图直接报错。出处写在 alpha-models.json_imageInputNote 和 runbook §B,runbook 里也写了直连实测后怎么改回来(commit fc2d59978)。

  • 平台模型:每一行都显式写 modalities.input: ["text"]。网关的两个聊天入口今天都返回 400、拒收图片,就算上游的 claude-* 能看图也一样。

  • 自定义节点:真源记录新增一个可选字段 imageInput(必须是 models 的子集,非空,不能重复;缺这个字段就表示一个都看不了)。providers.add 会透传它并做校验。用户声明了哪个模型不在 models 里,就在存密钥之前直接拒掉。这里不拿 models.dev 猜:自定义地址上的同名模型不一定是 models.dev 里那一个。

  • 每个注入的模型都显式写 modalities,不把决定留给回落。真引擎手段自证实测过:同一份注入去掉 modalities 之后,id 恰好叫 openai 的自定义节点会从 models.dev 继承到 image: true

本地门(worktree .worktrees/1420-byok-image-capability,产物已补齐:electron dist/path.txt 软链到主 checkout,alpha_fence.node 在本树用 scripts/build-fence-addon.ts 现编)

bash scripts/alpha-check.sh(rebase 到 origin/alpha@b03e79da1 之后):

▶ [1/14] north-star guard (zero upstream edits)
    ✓ zero upstream package edits
▶ [4/14] typecheck (alpha packages: contracts-consumer + ext + ui-mac + opencode + core + schema)
    ✓ typecheck
▶ [5/14] contract lock + unit tests
 5329 pass
 0 fail
Ran 5329 tests across 383 files. [350.11s]          ← ui-mac bun test src
✓ 222 个闸门文件全部在位且真的跑过(条数与登记精确一致)
    ✓ docs links (2 个 Markdown)
        ✓ packages/ui-mac/src/main 59126 行(= 基线)
        ⚠ 超阈值:packages/ui-mac/src/main/ext-config.ts 1598 行 > 800
exit=0

base fail-set 对照:同一棵树、同一套补齐的产物,在 base 8c8867c90 上跑 cd packages/ui-mac && bun test src,结果是 5321 pass / 0 fail。本分支是 5329 pass / 0 fail,多出来的 8 条都是新用例,没有新增失败。

新判据(先证明它们能变红)

  • image-input-capability.test.ts(新闸门文件,已登记):起真引擎跑 models --verbose,逐个注入的模型读引擎最终认定的 capabilities.input.image,期望值是手写的字面量。
    • 手段自证臂:同一台引擎、同一份注入,只删掉 modalities。结果是直连模型全部退回 false,同名 openai 节点变成 true。这一臂同时就是 base 行为的样子(base 本来就不写 modalities)。
    • 在 base 源码上跑这个文件是红的。
  • alpha-models.test.ts:新增 4 条(39 → 43)。
    • 出货目录的完备性:每个直连模型都必须有显式的布尔值。
    • 注入逐行检查:平台每一行只有 text
    • 目录缺值时注入成看不了图。
    • providers.addimageInput 的完整链路,以及拒收的情况。
  • custom-provider-truth.test.ts:新增 2 条(8 → 10),覆盖 imageInput 的正样本和 4 种坏形状。
  • 已有断言里写死了 {name} 形状的,都补上了 modalities,共 7 处。这些正是动笔前预判会变红的那几条。

模块体积

  • scripts/module-size-ratchet.tsv 的 tree 行从 59,075 抬到 59,126(+51,全部来自本票,理由写在那一行里)。
  • ext-config.ts 超过 800 行阈值:本票只在里面加了 6 行添加时的子集校验。校验必须和其余准入逻辑放在同一个 validateProviderInput 里,才能拒在存密钥之前。没有顺手拆这个文件。

主动没做的事

  • 没有做添加表单里的“能看图”开关renderer/alpha-ui/model-picker-add.tsx)。这是 UI 改动,要先走设计稿流程。今天用户只能通过真源记录(<appData>/alpha-code-state/custom-providers/<env>.json)或 IPC 来声明。已有的表单不带这个字段,所以在界面上重新保存一个节点,会把之前手写的声明清掉(行为是回到看不了图,按 fail-closed 方向)。
  • 没有动识图调用本身([REQ-228][CODE] 看不了图的模型:贴图和读到的图片自动送云端识图,模型可按路径追问 #1419),也没有动平台模型目录的 schema。
  • 没有改 governedModelsBase(v2 底表仍然写 modalities.input: []):config 里显式写的值优先,v1 引擎和 v2 拷贝都会拿到注入里的 modalities。
  • 没有对任何上游实测“到底收不收图”。能力值以 models.dev 为准,有差异就以 models.dev 为准。
  • 没有开审计轮,也没有写 Project 字段。

🤖 Generated with Claude Code

jinjunnn and others added 3 commits September 23, 2026 22:40
…1420)

引擎判「能不能看图」的唯一字段是 capabilities.input.image(transform.ts unsupportedParts
读它;看不了的模型收到的图片被换成一句 ERROR)。它由 config 的 modalities.input 写入,没写
就回落到 models.dev 底表同 id 条目。此前注入面一个模型都不写 modalities ⇒ 全部 false。

- 直连目录(alpha-models.json):每个 provider 新增 modelsDev(出处,与 baseURL 同端点的
  models.dev provider id)与 imageInput(逐模型显式布尔,抄自 models.dev 2026-09-23 快照)。
  能看图:deepseek-flash / qwen3.8-max / kimi-k3 / kimi-k2.6。缺值 ⇒ 看不了图。
- 平台模型:显式 modalities.input=["text"](网关聊天入口拒收图片)。
- 自定义节点:真源记录新增可选 imageInput(⊂ models、非空、无重复),providers.add 透传并校验;
  不声明 ⇒ 看不了图。同名不等于同一个模型,不按 models.dev 猜。
- 每个注入模型都显式写 modalities,不留回落:否则 id 恰好叫 openai 的自定义节点会从
  models.dev 继承到 image:true(真引擎手段自证臂实测)。

判据:image-input-capability.test.ts 起真引擎 models --verbose 逐模型判最终值(手写期望);
alpha-models / custom-provider-truth 各加完备性、fail-closed、形状与 providers.add 链的用例。

Fixes #1420
Refs jinjunnn/alpha-work#105

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…1420)

协调方 2026-09-24 实测:OpenRouter 目录 deepseek/deepseek-v4-flash 的 input_modalities
= ["text"];带图请求(含 provider.only=["deepseek"] 官方端点)⇒ 404 "No endpoints found
that support image input"(15 个端点全部不支持图片);能看图的是另一个型号
deepseek-v4-flash-vision-exp。models.dev 与之矛盾,本机无 DeepSeek 直连 key 实测
deepseek-flash 这个直连 id ⇒ fail-closed 取 false(错标 false = 多一次云端识图;
错标 true = 用户贴图直接报错)。

- alpha-models.json:imageInput["deepseek-flash"] = false,_imageInputNote 写明这一处例外;
- runbook §B:写明不一致、证据与直连实测后怎么改回;
- 手写期望同步改(仍是独立字面量):alpha-models.test.ts 两处、image-input-capability.test.ts;
- gate-files.tsv 两行描述里的个数、CHANGELOG 里「能看图」的名单随之更正(条数不变)。

Refs #1420

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
由编排者代解冲突(owner 2026-09-24 授权先合本 session、再帮另一 session 合)。
两处冲突:
- CHANGELOG.md:两条都是新增条目,都保留。
- scripts/module-size-ratchet.tsv 的 tree 行:基线与此前全部理由取 alpha 那侧
  (59,762,含 #1436 / #1426 两次抬高),本票增量按合并后实测重算 = 59,813(+51)。
  分支上原写的 59,126 是按旧基数算的,直接取会让门红。

合并后 assert-module-size 三行全 = 基线,且 #1438 新加的「半份登记」交叉判据未告警。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@jinjunnn
jinjunnn merged commit 78e7514 into alpha Sep 24, 2026
5 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[REQ-228][CODE] 自带 key 的模型如实标注能否看图,能看图的不再多扣一次识图费

1 participant