Skip to content

fix(deepseek): 按模型能力放开图片输入,flash 家族不再被当成纯文本 - #796

Merged
coder-hhx merged 1 commit into
mainfrom
fix/deepseek-vision-input
Sep 11, 2026
Merged

fix(deepseek): 按模型能力放开图片输入,flash 家族不再被当成纯文本#796
coder-hhx merged 1 commit into
mainfrom
fix/deepseek-vision-input

Conversation

@coder-hhx

Copy link
Copy Markdown
Collaborator

Linked issue

Closes #795

Summary

DeepSeek 官方《图像理解》指南已明确 deepseek-flash 吃图片(base64 data URL / 外部 URL / Files API file_id 三种传法;input_image 可出现在 user / developer 消息与 function_call_output 的 output 中),并注明旧模型名 deepseek-v4-flash-vision-exp 已下线、其请求由最新 Flash 承接。但 LiveAgent 从 #502 / #554 起把 DeepSeek 一律声明成纯文本,图片被拦在三处,请求侧完全用不上这个能力。

本次把"DeepSeek 不吃图"这个过期假设收敛掉,图片能力一律交给 model.input 决定:

  • modelFactory.ts(能力声明):deepseek 分支原本写死 input: ["text"],并显式把 DeepSeek 排除在用户 inputModalities 覆盖之外(注释理由是"wire 层硬拒绝图片")。改为按模型 id 推断——flash 家族 ["text", "image"]、Pro 与更早模型仍 ["text"]——并让覆盖生效。官方只承诺 flash 家族,所以不跟着一起放开 Pro,避免产生新的虚假能力声明;中转端点不吃图时用覆盖改回 ["text"]
  • deepSeekNative.ts(协议层):删掉 assertNoUserImageInput 的无条件硬抛,工具结果图片从"强制降级"改为按 model.input 门控。硬抛正是 fix(providers): 纯文本模型的工具结果图片降级为说明文字,不再让会话永久失败 #730 修过的那类故障成因:一张图之后,用户消息/工具结果永久留在历史里,每一轮都在本地被同一断言拦下,会话实质报废。纯文本模型(Pro,或被覆盖成 ["text"] 的中转)仍降级为说明文字,pi-ai 也会给不支持的图片补占位文本,两条路都不会 400。
  • nativeResponsesAttachments.ts(附件内联):原本只放行 codex / xai。放行 DeepSeek,图片附件首次请求即以 input_image 内联(不再先让模型去 Read 本地路径再等一轮),并给 DeepSeek 用专属的 upload 指令文案("DeepSeek Responses request",此前会误写成 OpenAI)。PDF 仍退回 Read:指南只用 file 描述图片,没有承诺 PDF 的 document 结构,supportsNativePdfInline 有意让 deepseek-responses 走 default 分支。
  • providerUtils.ts(设置页)providerSupportsModelInputModalitiesOverride 加入 deepseek,设置里可对单个 DeepSeek 模型选择"自动 / 纯文本 / 文本+图片"。

刻意没动的地方:toolResultImageFallback.ts 的 helper 语义不变(只是调用点改成门控版);deepSeekAttachments.ts 的大段粘贴内联不变。

Change scope

  • Modules: agent-gui / providers、agent-ui / settings
  • Key paths:
    • crates/agent-gui/src/lib/providers/runtime/modelFactory.ts
    • crates/agent-gui/src/lib/providers/deepSeekNative.ts
    • crates/agent-gui/src/lib/providers/nativeResponsesAttachments.ts
    • crates/agent-ui/src/pages/settings/providerUtils.ts
    • crates/agent-gui/test/providers/deepseek-native.test.mjs
    • crates/agent-gui/test/providers/native-responses-attachments.test.mjs
    • crates/agent-gui/test/settings/input-modalities.test.mjs

Screenshots / preview

无 UI 像素变化,因此没有 before/after 截图:本次唯一的界面可见效果是设置里 DeepSeek 模型的"输入模态"开关从隐藏变为可见(ProviderModal 复用 providerSupportsModelInputModalitiesOverride)。治理检查会因改动命中前端路径而要求配图,处理方式沿用 #730 的先例(该 PR 同为前端路径下的纯行为改动,被 bot 转 draft 后由维护者加 governance-exempt)。

以下为线格式层面的修复前后对照。

修复前 —— 工具结果图片被强制降级,模型只看到说明文字:

[1 image omitted from this tool result]
1. image/png (~20.6 KB)
The active model (deepseek-flash) does not accept image input, so the image bytes were not sent.
Do not repeat the same image-producing action expecting to see it. Rely on text output instead (for example a page snapshot or file text), or ask the user to switch to a vision-capable model.

修复前 —— 用户直接上传图片则在 fetch 之前抛错,请求根本不发出:

stopReason: "error"
errorMessage: "DeepSeek Responses does not support image input."

修复后 —— 同一 context 走 streamDeepSeekResponses(mock fetch 截获请求体),图片作为 input_image 到达线上:

{
  "role": "user",
  "content": [
    { "type": "input_text", "text": "describe" },
    { "type": "input_image", "detail": "auto", "image_url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUg…" }
  ]
}

修复后 —— 工具结果里的截图保留在 function_call_output 的 output 中(指南允许该位置带 input_image),不再需要模型换模型;纯文本模型仍降级:

model.input = ["text", "image"]  → payload 含 input_image: true,含降级说明: false
model.input = ["text"]           → payload 含 input_image: false,含 pi-ai 占位文本 "(image omitted: model does not support images)": true

Verification

  • cd crates/agent-gui && pnpm test:frontend → 3088/3088 通过(与改动前同一套全量前端用例,无回归、无快照更新)。
  • cd crates/agent-gui && pnpm buildtsc && vite build)通过;pnpm lint(biome check src/,333 文件)无问题。
  • cd crates/agent-gui && pnpm test:release → 9/9 通过。
  • cd crates/agent-gateway/web && pnpm build && pnpm lint && pnpm test → 718/718 通过(providerUtils.ts 属共享 @liveagent/ui,WebUI 侧一并验证;本地首次跑缺 jsdom,pnpm install --filter @liveagent/gateway-webui... 后全绿,与本次改动无关)。
  • 新增 / 更新用例:
    • deepseek-native.test.mjs:把原「DeepSeek rejects image input before sending a request」换成两条——视觉模型下图片以 input_image 上线;纯文本模型下请求照常发出、线上无图片字节、由 pi-ai 补占位文本。原「工具结果图片降级」用例保持不变并继续通过(模型 input: ["text"])。
    • native-responses-attachments.test.mjs:DeepSeek 图片内联、PDF 留在 Read 路径(断言只读了一次附件且是 image)、纯文本模型不读图片字节且 payload 保持同一性;另加一条锁定 providerId === "deepseek" 才装 hook。
    • input-modalities.test.mjs:原「deepseek keeps the hard text-only constraint despite the override」换成 id 推断 + 覆盖双向生效(flash 默认吃图、Pro 默认纯文本、覆盖可双向改),并把 providerSupportsModelInputModalitiesOverride("deepseek") 断言改为 true。

未验证的部分(需要真实 key):本次没有对 api.deepseek.com 发真实请求(本地无可用 key),上游对 deepseek-flash 之外 id 的视觉支持边界(如 deepseek-v4-pro)也没有实测。若线上表现与 id 推断不符,用设置里的输入模态覆盖即可纠正,无需改代码。

Pre-submit checklist

  • A requirement issue is linked(Closes [Bug] DeepSeek flash 家族已支持图片输入,但 LiveAgent 仍把 DeepSeek 一律当纯文本 #795
  • Synced with the target branch; no merge conflicts(基于 63f655d 新建分支)
  • The change is focused, with no unrelated modifications(工作区里 desktop-release.yml 的既有本地改动未纳入本 PR)
  • No secrets, tokens, or personal data included
  • Docs are updated for changes affecting user behavior, deployment, or configuration. —— 无仓库文档描述这些能力声明,行为与理由写在对应代码注释里。遗留一项:模型目录快照(catalog.generated.ts)里 DeepSeek 仍标 inputModalities: ["text"],设置页模态徽标会继续显示纯文本;该文件由 models.dev 生成(scripts/generate-model-catalog.mjs + 定时工作流),等上游数据更新即可,不影响请求路径。

官方《图像理解》指南已明确 deepseek-flash 支持图片(image_url / input_image /
Files API file_id,input_image 可出现在 user 消息与 function_call_output 的
output 中),旧模型名 deepseek-v4-flash-vision-exp 下线后由其承接。此前
LiveAgent 把 DeepSeek 一律声明成纯文本,图片被拦在三处:

- modelFactory 的 deepseek 分支写死 input: ["text"],用户的 inputModalities
  覆盖也被显式排除。改为按模型 id 推断(flash 家族吃图、Pro 与更早模型仍纯
  文本),并让覆盖生效,中转端点可改回 ["text"]。
- deepSeekNative 对 user 消息里的图片直接抛错,并强制把所有工具结果图片降级
  为说明文字。硬抛会让一张图之后每一轮都失败(#730 之前的老毛病),改为按
  model.input 门控:声明了 image 的模型保留图片,纯文本模型继续降级。
- nativeResponsesAttachments 只放行 codex/xai 走 input_image 内联。放行
  deepseek,图片附件首次请求即内联;PDF 仍退回 Read(指南只用 file 描述图片)。
@StackCairn
StackCairn marked this pull request as draft September 11, 2026 16:23
@github-actions

Copy link
Copy Markdown
Contributor

PR governance checks failed — this PR has been converted to draft.

  • UI change without screenshots: this PR modifies frontend code. Please add before/after screenshots or a recording under "Screenshots / preview" in the PR body.

Fix the items above, then click Ready for review to re-run the checks.

@coder-hhx coder-hhx added the governance-exempt Skip PR governance checks label Sep 11, 2026
@coder-hhx
coder-hhx marked this pull request as ready for review September 11, 2026 16:24
@coder-hhx
coder-hhx merged commit 479c9fc into main Sep 11, 2026
9 of 10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

governance-exempt Skip PR governance checks

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] DeepSeek flash 家族已支持图片输入,但 LiveAgent 仍把 DeepSeek 一律当纯文本

1 participant