摘要
使用 OpenAI 兼容的 provider 时,Computer Use 截图(mcp__computer-use__screenshot、zoom)返回空,而基于文本的工具(如 cursor_position)正常工作。截图本身已正确捕获(生成真实的 JPEG),但在到达上游模型前被悄悄剥掉了。
根因
sidecar 的 Anthropic → OpenAI 消息转换器丢弃了 tool_result 里的图片块。
转换器由 apiFormat 选择:
if (_.apiFormat === "openai_chat") return await II3($, A, _.apiKey, H, w, J);
else return await mI3($, A, _.apiKey, H, w, J, j);
openai_chat → II3(/v1/chat/completions),通过 $w3/Ow3 构建消息。tool_result 分支只保留文本:
else if (_.type === "tool_result") {
let $ = typeof _.content === "string"
? _.content
: Array.isArray(_.content)
? _.content.filter((H) => H.type === "text").map((H) => H.text).join(`\n`)
: "";
K.push({ role: "tool", tool_call_id: _.tool_use_id, content: $ });
}
openai_responses → mI3,其 tool_result 转换器 Qa4 也只保留 type === "text":
function Qa4(q) {
if (typeof q === "string") return q;
if (!Array.isArray(q)) return "";
return q.map((K) => {
if (K && typeof K === "object" && "type" in K) {
if (K.type === "text" && typeof K.text === "string") return K.text;
}
return "";
}).filter(Boolean).join(`\n`);
}
此外,对 baseUrl 含 opencode.ai 或 deepseek 的 provider,UW5(baseUrl)===true 会强制 imageContentMode="text_only",连用户消息里的图片也会被替换成占位符(zw3)——所以丢的不只是 tool_result 图片:
function UW5(q) {
return /(^|[./-])deepseek([./-]|$)/i.test(q) || /(^|[./-])opencode\.ai([:/]|$)/i.test(q);
}
function uI3(q) { return UW5(q); } // → imageContentMode = "text_only"
证据
- 截图工具确实返回真实图片。 Anthropic 格式的请求里包含:
{
"type": "tool_result",
"tool_use_id": "call_00_q7th5rjrFvmNGEVkwTt15015",
"content": [
{ "type": "image", "source": { "type": "base64", "media_type": "image/jpeg", "data": "/9j/4AAQSkZJ..." } }
]
}
-
请求被路由到 OpenAI-chat 路径。 对 { format: "openai_chat", baseUrl: "https://opencode.ai/zen/go/" } 这样的 provider,出站 URL 是 https://opencode.ai/zen/go/v1/chat/completions。
-
隔离复现 $w3 确认了该行为:
- 输入
tool_result 为 [text, image] → 输出 { role: "tool", tool_call_id: "...", content: "Screenshot taken" }(图片消失)。
- 输入
user 消息为 [text, image] → 输出保留 { type: "image_url", image_url: { url: "data:image/jpeg;base64,..." } }。
所以图片在 user 消息里被保留(vision 模式),但在两种转换器路径的 tool_result 里都被丢弃。
影响
- 任何 OpenAI 兼容 provider(chat 或 responses 格式)都会静默丢弃作为 tool result 返回的图片。
- Computer Use 的
screenshot / zoom 一直为空——模型只看到一个纯文本的 tool result。
建议的修复
- 在
$w3/Ow3(以及 Qa4)里,保留 tool_result 内的 image 内容块,例如在 tool 内容数组里输出为 OpenAI 的 { type: "image_url", image_url: { url: "data:..." } },而不是只过滤 text。
- 重新审视支持 vision 流式输出的 provider 上
UW5/uI3 的 text_only 门控。
摘要
使用 OpenAI 兼容的 provider 时,Computer Use 截图(
mcp__computer-use__screenshot、zoom)返回空,而基于文本的工具(如cursor_position)正常工作。截图本身已正确捕获(生成真实的 JPEG),但在到达上游模型前被悄悄剥掉了。根因
sidecar 的 Anthropic → OpenAI 消息转换器丢弃了
tool_result里的图片块。转换器由
apiFormat选择:openai_chat→II3(/v1/chat/completions),通过$w3/Ow3构建消息。tool_result分支只保留文本:openai_responses→mI3,其tool_result转换器Qa4也只保留type === "text":此外,对
baseUrl含opencode.ai或deepseek的 provider,UW5(baseUrl)===true会强制imageContentMode="text_only",连用户消息里的图片也会被替换成占位符(zw3)——所以丢的不只是tool_result图片:证据
{ "type": "tool_result", "tool_use_id": "call_00_q7th5rjrFvmNGEVkwTt15015", "content": [ { "type": "image", "source": { "type": "base64", "media_type": "image/jpeg", "data": "/9j/4AAQSkZJ..." } } ] }请求被路由到 OpenAI-chat 路径。 对
{ format: "openai_chat", baseUrl: "https://opencode.ai/zen/go/" }这样的 provider,出站 URL 是https://opencode.ai/zen/go/v1/chat/completions。隔离复现
$w3确认了该行为:tool_result为[text, image]→ 输出{ role: "tool", tool_call_id: "...", content: "Screenshot taken" }(图片消失)。user消息为[text, image]→ 输出保留{ type: "image_url", image_url: { url: "data:image/jpeg;base64,..." } }。所以图片在 user 消息里被保留(vision 模式),但在两种转换器路径的
tool_result里都被丢弃。影响
screenshot/zoom一直为空——模型只看到一个纯文本的 tool result。建议的修复
$w3/Ow3(以及Qa4)里,保留tool_result内的image内容块,例如在 tool 内容数组里输出为 OpenAI 的{ type: "image_url", image_url: { url: "data:..." } },而不是只过滤text。UW5/uI3的text_only门控。