Repository navigation
【BUG】read_image 图片块在 openai-responses 协议下未做能力适配,触发 400 且会话永久卡死 #11311
Replies: 3 comments
|
这里的判据不是协议,是模型有没有声明图片输入能力。
if (modelInfo.inputModalities !== undefined
&& !modelInfo.inputModalities.includes('image')
&& projectedMessages.some(message => contentHasImage(message.content))) {
projectedMessages = projectImagesForTextModel(projectedMessages)
}只有模型明确声明「不包含 image」时才降级成文字形式。 所以「openai-responses 未做能力适配」这个说法要改一下:适配是按模型能力做的,不按协议。想让 DSH 降级,就在模型声明里去掉 image——这样请求会带上 会话卡死那半是另一回事:400 之后那条图片块还留在历史里,之后每条请求都会重复它,这条没有自愈路径。 我只读了源码( |
|
PerryLink's reading above is correct, and I re-measured it on a newer line — so this is a confirmation, then the part that came from running it rather than reading it. Confirmed at One place I would push back. "Remove image from the model declaration" is the report's workaround 2, and it is global: the same model id, served by a route that can carry images, loses them there too. The reporter's §4.1 is what makes that concrete — one provider, two protocols, The second half — the block stays in durable history and is replayed into every later request, including pure-text turns and every compaction of the same prefix — has no recovery path, which I agree with. What I published, so nobody has to choose between losing images everywhere and losing the session. dsh plugin add github:argszero/dsh-image-route-guard#v0.1.0- id: image-route-guard
config:
routes:
- provider: agnes
model: agnes-3.0-flash # omit to cover every model on that providerIt projects image blocks to the harness's own placeholder before dispatch, only on the routes you declare image-incapable. It reproduces core's The honest limit. This removes the poisoned block from the request. It does not teach the provider path to degrade images, and it cannot detect by itself which protocols can carry one — an undeclared route behaves exactly as before. The real fix is the predicate above consulting the route; when it does, the mount becomes unnecessary. Measured on If a session is hit by an image the route rejects at runtime rather than one it has no wire shape for, |
|
You're right, and the gap you name is real. The predicate cannot express "this route cannot carry an image", so my "drop image from the model declaration" was too blunt — it would also turn off images on routes that do carry them. Worth pinning where the information actually lives, because it decides how small the real fix is. The predicate reads Until that exists your plugin is the honest workaround, and I'd put your sentence in the report title: declared per model, carried per route. Source read at |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
1. 摘要(DSH 侧问题)
DSH 中配置为 pi-ai
openai-responses协议的 provider(本例为 agnes /agnes-3.0-flash),在read_image工具结果含本地图片块{"type":"image","attachment":{...}}后,该图片块被原样拼入下一次请求的input,网关拒绝整条请求(400json_parse_error,input反序列化失败)。该图片块停留在会话历史里,导致该会话之后所有请求(含纯文本)全部 400,会话永久卡死、无恢复路径,用户只能新建会话。DSH 的
dsh-llm已具备图片降级原语(textOnlyImageText、projectImagesForTextModel、requiredImageOffload等),但 pi-ai 的 openai-responses 路径并未在组装请求时按 provider/协议的图片输入能力对图片块做降级或剔除,也未对这类 400 提供会话自愈。2. 环境
https://api.agnes-ai.cn/v1),模型agnes-3.0-flash,协议 pi-ai openai-responsessession-3188ae7b-ea39-460a-a6c5-098310da31f63. 复现步骤(DSH 内操作)
openai-responses的 provider 会话中,让 agent 调用read_image读任意本地图片{"type":"image"}块)4. 证据
出事前该会话已正常执行 30+ 次 LLM 请求(56K token 历史、含 reasoning/thinking 回放),全部成功;第一次 400 恰好出现在
read_image图片块首次进入历史那一步,唯一新变量即该图片块。涉事工具结果内容:
[ {"type": "text", "text": "<path>…010.webp</path>\n<type>image</type>\nimage/webp image, 512x512 px, 33116 bytes"}, {"type": "image", "attachment": {"attachmentId": "sha256:3ffcabdc…", "mediaType": "image/webp", "bytes": 33116, "width": 512, "height": 512, "name": "010.webp"}} ]DSH 报出的三次 400 原文(网关侧
input反序列化失败):line 1表明请求体为单行 JSON,body ≥ 422KB4.1 网关侧实测(环境佐证,2026-10-28,curl + 有效 key)
POST /v1/chat/completions纯文本POST /v1/chat/completions+ 公开图片 URLimage_tokens: 176POST /v1/chat/completions+ base64 data URLimage_tokens: 176POST /v1/responses纯文本(连试 3 次)bad_response_bodyPOST /v1/responses+ 图片块(3 种形态)type:'unknown'GET /v1/models即该 provider 的 Responses 端点当前对图片块不可用,Chat Completions 端点完整可用。此事实仅用于说明「该协议路径下图片块没有合法形态」,不是本 bug 的归因主体;DSH 侧的问题在于:即便目标 provider 的某协议不支持图片块,也应在请求组装时降级/剔除图片块(或按 provider 声明的可用协议发送),而不是让图片块卡死整个会话。
5. 期望行为(DSH 侧)
[image: 010.webp 512x512 33116B],复用dsh-llm已有原语),支持时以该协议接受的形态发送6. 用户侧规避(配置层面,非 DSH 缺陷)
本例的 agnes provider 属个人配置问题:桌面端把
api声明为openai-responses(该端点对图片块不可用),而 Chat Completions 端点完整可用。规避方式(无需等 DSH 修复):api改为openai-completions(chat 端点文本与图片均可用,本地 base64 图片 200)input: [text]),避免 DSH 走图片输入路径All reactions