问题描述
在使用 Codex Desktop 对接 sub2api 的 OpenAI Responses 链路时,连续会话的稳定性和连续性存在明显问题,主要分为两类:
- WS (ctx_pool) 经常中途断流/重连
- HTTP /responses` 也会出现“像重新回答/上下文重复”的感觉
这不是“没有启用 WS”的问题,而是在已经启用后,实际运行中仍会频繁暴露会话恢复与续接上的不稳定。
当前现象
A. WebSocket 链路
客户端经常出现:
- stream disconnected before completion: websocket closed by server before response.completed
- 持续 Reconnecting...
- 回答未完成就中断
对应服务端日志中常见:
- 1013 upstream websocket is busy, please retry later
- context deadline exceeded
- upstream continuation connection is unavailable; please restart the conversation
- preferred connection unavailable
- �roken pipe
B. HTTP /responses 链路
即使不使用 WS,也仍然会感觉:
- 某些 turn 像“重新回答了一遍”
- 连续对话的上下文衔接不如预期
- 有时会出现明显的“重复解释 / 像重新开了一轮”体验
关键代码/行为判断
1. WS (ctx_pool) 存在自动恢复 / 重放逻辑
从 openai_ws_forwarder.go 和 openai_gateway_service.go 看,当前实现中存在这些恢复行为:
- ingress_ws_prev_response_recovery ... action=drop_previous_response_id retry=1
- ingress_ws_preflight_ping_recovery ... action=drop_previous_response_id_retry
econnect_prev_response_recovery ... action=drop_previous_response_id retry=1
- ingress_ws_function_call_output_prev_infer ... action=set_previous_response_id
也就是说,当 continuation / previous_response_id 续接失败时,当前实现会尝试:
- 自动回填 previous_response_id
- 或者在某些失败场景下去掉 previous_response_id 后重试当前 turn
这会带来一个副作用:
当客户端期望“继续上一轮”时,服务端实际上可能把当前 turn 以“新一轮 create”形式再发一次,导致用户体感上出现上下文重复、重新解释、重复回复。
2. HTTP /responses 会主动过滤 previous_response_id
从 openai_gateway_service.go 看,当前实现明确有这样一条规则:
仅在 WSv2 模式保留 previous_response_id,其他模式(HTTP/WSv1)统一过滤。
也就是说,在 HTTP /responses` 下,客户端即使带了 previous_response_id,也会被删除。
这会导致:
- 连续会话不能按 OpenAI Responses 的原生方式续接
- 上游更像收到一条新的请求而不是上一轮的 continuation
- 从体感上看,就会像“重新回答”“上下文不够连贯”“有时像重复”
3. HTTP 流式终止不完整时,也会放大重复感
HTTP 流式路径里还能看到:
- stream usage incomplete: missing terminal event
- stream usage incomplete after disconnect
这类问题本身不一定由服务端重放整轮,但它很容易诱发上层/客户端再次发起请求,最终让用户感觉像“回答又来了一遍”。
我认为当前最值得优化的方向
A. 给 WS 恢复逻辑增加更明确的开关/模式
现在看起来虽然已经有:
- gateway.openai_ws.ingress_previous_response_recovery_enabled
但还不够,因为某些 preflight_ping_recovery 路径仍会走 drop_previous_response_id_retry。
建议:
- 增加一个更清晰的总开关,例如:
- 完全禁止“去掉 previous_response_id 后重试当前 turn”
- 把 previous_response_not_found recovery 和 preflight_ping recovery 分成独立开关
- 提供 fail-close 模式:
- 一旦 continuation 失效,就直接报错给客户端,而不是尝试改写当前 turn 再重放
B. HTTP 模式不要无条件删掉 previous_response_id
当前 HTTP /responses` 之所以会有“像重复回复”的体感,一个重要原因就是它直接过滤了 previous_response_id。
建议:
- 不要对 HTTP 一刀切删除 previous_response_id
- 至少提供配置开关:
- 允许在兼容路径中透传 previous_response_id
- 如果确实因为某些上游不兼容需要过滤,也应该做成可控策略,而不是硬编码默认删除
C. 优化 ctx_pool 下 preferred connection 生命周期
从日志看,很多断流最终都落到:
- preferred_conn_unavailable
- continuation connection is unavailable
建议重点排查:
- preferred connection 何时被提前释放/标记 broken
- preflight ping 失败后是否可以更保守地恢复,而不是直接改写 turn 内容
- continuation 丢失时,是否应把错误原样返回,而不是 silently 改写成新建请求
结论
当前 sub2api 的 OpenAI/Codex 连续会话问题,不只是“WS 不稳定”这么简单,而是:
- WS 有较强的自动恢复/重放逻辑,可能导致上下文重复或像重新回答
- HTTP 又会主动过滤 previous_response_id,天然削弱原生 continuation 能力
这两个方向叠加后,用户就很容易感受到:
- 长对话连续性不稳定
- 有时中途断流
- 有时像重新回答/重复回复
希望作者能从“会话连续性”角度系统梳理一下,而不只是把它当成单点网络不稳定问题。
问题描述
在使用 Codex Desktop 对接 sub2api 的 OpenAI Responses 链路时,连续会话的稳定性和连续性存在明显问题,主要分为两类:
这不是“没有启用 WS”的问题,而是在已经启用后,实际运行中仍会频繁暴露会话恢复与续接上的不稳定。
当前现象
A. WebSocket 链路
客户端经常出现:
对应服务端日志中常见:
B. HTTP /responses 链路
即使不使用 WS,也仍然会感觉:
关键代码/行为判断
1. WS (ctx_pool) 存在自动恢复 / 重放逻辑
从 openai_ws_forwarder.go 和 openai_gateway_service.go 看,当前实现中存在这些恢复行为:
econnect_prev_response_recovery ... action=drop_previous_response_id retry=1
也就是说,当 continuation / previous_response_id 续接失败时,当前实现会尝试:
这会带来一个副作用:
2. HTTP /responses 会主动过滤 previous_response_id
从 openai_gateway_service.go 看,当前实现明确有这样一条规则:
3. HTTP 流式终止不完整时,也会放大重复感
HTTP 流式路径里还能看到:
这类问题本身不一定由服务端重放整轮,但它很容易诱发上层/客户端再次发起请求,最终让用户感觉像“回答又来了一遍”。
我认为当前最值得优化的方向
A. 给 WS 恢复逻辑增加更明确的开关/模式
现在看起来虽然已经有:
但还不够,因为某些 preflight_ping_recovery 路径仍会走 drop_previous_response_id_retry。
建议:
B. HTTP 模式不要无条件删掉 previous_response_id
当前 HTTP /responses` 之所以会有“像重复回复”的体感,一个重要原因就是它直接过滤了 previous_response_id。
建议:
C. 优化 ctx_pool 下 preferred connection 生命周期
从日志看,很多断流最终都落到:
建议重点排查:
结论
当前 sub2api 的 OpenAI/Codex 连续会话问题,不只是“WS 不稳定”这么简单,而是:
这两个方向叠加后,用户就很容易感受到:
希望作者能从“会话连续性”角度系统梳理一下,而不只是把它当成单点网络不稳定问题。