Skip to content

[PR] fix: preserve non-image Google inline media during image shedding - #6184

Open
Afloat16 wants to merge 1 commit into
ranxianglei:masterfrom
Afloat16:2026-11-10_shed-image-mime-only
Open

Afloat16 wants to merge 1 commit into
ranxianglei:masterfrom
Afloat16:2026-11-10_shed-image-mime-only

Conversation

@Afloat16

Copy link
Copy Markdown
Contributor

Fixes #6176

Problem

The Google collector treats every inlineData part as an image. Gemini also carries audio, video and documents in that field, so a byte or image-count limit can replace those parts with an image-omitted note. Undeclared media is likewise assumed to be PNG without evidence.

Changes

Collect Google inlineData only when its declared MIME type is image/*. Preserve non-image and unknown media in both byte and count shedding; image archiving and protected-tail behavior continue as before.

Google's Part/Blob schema documents these non-image media types: https://ai.google.dev/api/generate-content#Blob

Validation

  • Rebased onto master e3220adc0f5412af83163666ca70a6afa05e3247 (package version 0.1.213). The intervening [PR] thirdparty-scan: flag dsh-live-compact as a known conflict (#5984) #6166 changes only third-party plugin detection, its tests, announcements and configuration docs; none of this patch's source or test files changed. The complete patch is byte-identical to the locally validated patch on 33104b1f9545caeb0344193009917ceef9969682 and applies cleanly.
  • Negative control on 33104b1f9545caeb0344193009917ceef9969682: 8 new regression cases still fail before the production fix.
  • Independent source tree based on 33104b1f9545caeb0344193009917ceef9969682 (5f3c2d54180ecacc9a5f664a970e236caa0d25d5): 30/30 focused tests passed, with no failures, skips or cancellations.
  • Full project TypeScript checking and the standard npm run build passed on the temporary integration tree e186774d3428c85e2a4974c6e609b2a7381d6153 containing the seven independently submitted fixes on 33104b1f9545caeb0344193009917ceef9969682. The repository compiler options and include set are unchanged.
  • git diff --check passed. Local runtime: Node.js 24.22.1 on Linux. Full cross-platform remote CI remains pending.

This change filters the wire collector and is independent of the image encode-cache MIME key in #6109. No configuration or kernel changes.

@ranxianglei

Copy link
Copy Markdown
Owner

🤖 Powered by ework · qwen3.8-27b

(ai: vllm-qwen/qwen3.8-27b)

审核结论先行:方向正确、修到根因、测试有牙,可合入;但按 §7.4 不能自动合并——需 owner 人工批准(理由见第 5/7 问与末尾)。无阻塞项,仅两处非阻塞小提醒。

查重:不重复。 同作者相邻的 #6147(把 Gemini 工具响应里的图计入预算/摘要,方向是"多算进去")与 #6109(downscale 缓存键带上声明的 mime)机制不同、且都不碰 src/egress-shed.ts——三者文件互斥,无合并顺序竞争。本 PR 是 #6176 的正主修复。


结构化八问

1. 是什么问题
Google(Gemini) 车道把每个 inlineData part 一律当图片处理。但 Gemini 的 inlineData 也承载音频/视频/PDF 等二进制(Blob schema 明确列出)。于是当出站字节预算(#3701/#3909)或图片数量上限(#4124)触发丢弃时,这些非图片内容会被替换成 [image … omitted for size limit] 占位符——模型那头就再也拿不到这段音频/文档了。另外未声明 mimeType 的内容被"无证据地"当成 PNG。

2. 什么原因导致的
根因在采集器 collectOccurrences(src/egress-shed.ts:141)的 google 分支:它对任何非空 inlineData.data 都建 occurrence,并在 mimeType 缺失时兜底填 "image/png"(旧第 172 行)。这个函数是唯一喂给两条丢弃入口的地方——字节丢弃 shedWireImages(第270行)和数量丢弃 shedWireImagesToCount(第363行)都只经它取 occurrence。所以"把非图片当图片"这一错误假设集中在这一个函数里,属单点根因,不是表面现象。

3. 影响范围与严重程度
触发条件较窄:①Google 上游 ②请求超出字节预算或图片数上限 ③正文带非图片/未声明 mime 的 inlineData。命中时该段媒体只在出站副本上被替换成占位符,模型本轮看不到它——属功能性降级而非永久数据丢失:原始字节仍留在会话存储+入站图片索引里(read_original_image / decompress 仍可取回),客户端自身历史也不受影响(outbound-copy-only 纪律)。严重度我判 p2(特定条件下明显缺陷、可用但有坑),比 reporter 提的 p1 略低——因为不是"不能用/想卸载"级别且可恢复;最终定级留给 owner。范围标签 scope/wiring(外部 API 边界/出站处理)。

4. 怎么修复的
在采集器 google 分支加一道前置门:只有 inline.mimeType 是字符串且匹配 /^image\//i 才建 occurrence,否则直接跳过(保留原样);同时删掉 "image/png" 兜底默认(mediaType 直接取已校验过的 inline.mimeType)。因为是单点采集器,字节与数量两条路径一次性都修好。修的是产生错误假设的源头(疏),不是在中途过滤 payload(堵)——同一根因换个形状再来,这修法依然成立。

5. 是否有回归问题
逐条过既有行为:

6. 是否破坏缓存
不涉及缓存失效策略。丢弃本就是 outbound-copy-only + 确定性(oldest-first + 固定 ladder)以保前缀缓存稳定;本次只是缩小了"哪些 part 参与丢弃"的集合,不改变确定性,反而减少了对非图内容的误改。

7. 是否改了设计方向
架构/边界不变,仍是同一采集器+两档丢弃,无转向。但出站消息形态在两模式下都受影响——shedEgressIfOverBudget 同时被 relay 车道(src/server/relay.ts:874/1327)与 loop(src/loop/core.ts:573)调用,plugin/proxy 都过同一 egress shed。

8. 是否引入了新的配置
无新增 env/config/默认值。纯行为修正。(注意:删除了 image/png 这个隐式默认值——正是要 owner 拍板的点。)


为什么不能自动合并(§7.4)

diff 干净、单模块、纯 fix、带回归测试、引用 Fixes #6176,但两条踩中"必须人工"红线:

  • 删除了 fallback/默认值(image/png 兜底)——§7.4 明列"任何 fallback/默认值变更(产品决策)"归人工;
  • wire/message-shape 两模式都受影响——归人工。
    建议:CI 转绿后由 owner 人工 review+合并。

非阻塞小提醒(可不改)

  • issue 正文说回归覆盖落在三个测试文件,实际 PR 只新增 tests/egress-shed-media.test.ts(另两个 issue4124-*/issue5745-* 是 master 已有存量文件,未被改动)。新文件 8 个用例已完整覆盖 字节/数量 × 纯非图/混合 场景,覆盖足够,仅描述与实际略有出入。
  • 判定用内联正则 /^image\//i(大小写不敏感),而 image-restore.ts:169 用大小写敏感的 startsWith("image/");仓库没有统一的 isImageMime 助手。本次保持最小改动是对的,日后此类判定再增可考虑抽共享助手,不必塞进本 PR。

判定

LGTM,可合入;需 owner 人工批准(不可自动合并)。 修法打到根因、范围收敛、测试有牙、无重复无竞争、无意外回归;唯一要 owner 点头的是"去掉 image/png 兜底 + 非图/未知内容改为保留"这一产品取舍。CI 绿后即可合。

This branch has not been deployed

No deployments
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.

[Bug] Google audio, video and PDF inlineData can be discarded by image shedding

2 participants