fix(responses): normalize multimodal content in tool call output items - #265
Conversation
|
评审: head
我核过下游,你这条修的是真东西:
M1(请修)—
|
|
已按评审意见全部修改并推送到 PR 分支(commit
单元测试 40/40 持续全绿通过。请重跑测试与对抗验证 |
|
复核:三条我都核过,都成立, head
M1 成立 ✓(但那个数字是不变式,不是常量)
一处建议:它是 M2 数字对、理由不成立 —— 这条请改
所以:
如果目标真是 15–25 MB,那要动的是
|
|
感谢指正!应用层 已按意见完成最后一轮修正并同步推送:
目前 PR 描述、代码注释与 40/40 单元测试均已就绪,请查阅合入,辛苦! |
|
评审:最后两条都到位。注释写成了不变式,正文把真上限改成了 head
合完按实测刷了 4 个覆盖 |
User-visible: consecutive same-role Connect text no longer trips run>=3 invalid_argument (#270); Responses tool-output images reach Connect tag 10 and docker nginx is 32m/900s with the real cap still MAX_BODY_SIZE=10MB (#265); local import finds Devin desktop userData and a 160KB windsurfAuthStatus is no longer silently skipped (#264); swe-2 is in the Connect catalog and gpt-5.6 Local is opt-in (#266). No API breakage. ACU ^22 still default off. FREE_TIER_SELECTOR still swe-1-6-slow. Mutation specs stay 49/588; baselines were remeasured at merge.
改了什么 / What changed
src/handlers/responses.js中,将function_call_output与custom_tool_call_output的output字段通过normalizeMessageContent()转译为结构化消息内容,而非直接使用stringifyMaybe()强行转为扁平文本(本修复针对 DEVIN_CONNECT 路径生效)。normalizeMessageContent()中增加非 content-block 数组的防护逻辑:当数组元素不包含标准 content block 特征(type字段)时(如['file1.txt', 'file2.txt']),安全回退到原有stringifyMaybe()行为,保证通用数据结构不被丢弃。nginx.conf中为内置反向代理配置client_max_body_size 32m;(解除 Nginx 默认 1m 瓶颈,放行至应用层)以及 900s 代理超时(作为不变式严格高于应用层DEVIN_TIMEOUT_MS默认 600s 阈值)。作用范围与边界说明 / Scope & Boundary
src/devin-connect.js后,多模态图像正常被extractInlineImages提取并分流至图像通道(#10 images)。tool-emulation.js:1097-1099仍会将数组形态的 tool outputJSON.stringify回文本,且client.js的extractImages目前仅作用于最后一条消息)。本次改动不涉及 Cascade 侧深层重构,特此明确边界,避免后续误解为全局所有分支均已重构。为什么 / Why
在 Responses API 规范中,工具调用的结果(
function_call_output.output)支持多模态内容数组(例如[{type: 'input_text', text: '...'}, {type: 'input_image', image_url: 'data:image/...;base64,...'}])。此前 WindsurfAPI 在
responsesToChat阶段对item.output统一调用stringifyMaybe(),导致包含 Base64 图像的工具输出被整体序列化为巨型文本字符串:ImageData);client_max_body_size(默认仅 1m)。任何超过 1 MB 的多模态负载在到达应用层之前即被 Nginx 拦截抛出413 Request Entity Too Large。Nginx 参数依据与实测边界 / Nginx Parameters Rationale & Boundaries
proxy_read_timeout 900s;/proxy_send_timeout 900s;:注释与配置已明确为系统不变式(必须严格大于应用层DEVIN_TIMEOUT_MS,默认 600s)。消除两层同时超时的竞态条件,确保应用层永远优先返回明确的upstream_timeout,避免客户端偶发收到 Nginx 的 504。client_max_body_size 32m;实测边界:server.js: MAX_BODY_SIZE = 10 * 1024 * 1024。因此,Nginx 侧只需配置≥ 10m即可完全放行应用层支持的最大负载;收紧至 32m 既完全覆盖了应用层 10m 上限,又避免了 100m 带来的不必要攻击面与内存暴露。测试 / Testing
单元测试:
在
test/responses.test.js补充了针对function_call_output/custom_tool_call_output多模态结构转译及普通数组回退保护的断言:测试结果:40 tests, 4 suites, 40 pass, 0 fail.
端到端多模态实机调用测试:
通过 Responses API 发起工具调用读取多张实际图片,模型均能精准解析图像内容,无 Token 膨胀及报错。
对 Nginx 端口发送大体积 Base64 图像负载(5MB+),成功穿透反代进入应用层并返回 200,不再被 Nginx 413 提前截断。
Checklist
Co-Authored-By、Generated with等)