Skip to content

fix(stream): 流中断时干净收尾,避免拼接错乱回复 - #92

Open
Sunsh1neY wants to merge 1 commit into
Sophomoresty:mainfrom
Sunsh1neY:fix/stream-graceful-close
Open

fix(stream): 流中断时干净收尾,避免拼接错乱回复#92
Sunsh1neY wants to merge 1 commit into
Sophomoresty:mainfrom
Sunsh1neY:fix/stream-graceful-close

Conversation

@Sunsh1neY

Copy link
Copy Markdown

问题

通过 HTTP 代理访问上游时,流式响应偶发中途断开(日志表现为 [SSL] record layer failure,常见于代理复用/回收 keep-alive 连接后)。此时 generate_stream 的重试逻辑会整段重新生成,而已流式输出的内容无法撤回,导致两种用户可见的故障:

  1. 回复说一半就停:重新生成的内容与已输出前缀不一致,触发 Gemini stream content changed during retry,重试耗尽后流被静默丢弃(无 finish_reason、无结束标记);
  2. 前后矛盾的拼接回复:若重新生成的内容恰好以已输出文本为前缀(同 prompt 开头通常一致),校验放行后继续输出——客户端最终显示的是两次不同生成的拼接,逻辑跳跃、前后矛盾。

修复

  • generate_stream:一旦有部分内容已输出,跳过重试并干净结束流。客户端收到格式完整的响应(finish_reason: "stop" + data: [DONE]),可自行发起续写;
  • _get_httpx_client:禁用 keep-alive 连接复用(Connection: close),避免复用已被代理/上游关闭的连接导致流中途 SSL 断裂。代价为每请求一次额外的 TLS 握手,对话场景无感。

测试

  • 现有 18 个测试用例全部通过(pytest tests/ -q);
  • 实际环境复现验证:代理链路抖动触发中断时,日志记录 Stream interrupted after partial output (N chars), ending cleanly,客户端拿到正常收尾的流;连续 3 次长回复流式请求均干净结束,不再出现重试拼接。

上游连接经代理中断(如 "[SSL] record layer failure")后,原逻辑会整段
重新生成并重试。由于已流式输出的内容无法撤回,重试要么因前缀校验失败
("Gemini stream content changed during retry")导致流被静默丢弃——客户端
看到回复说一半就停;要么在重试内容恰好以已输出文本为前缀时,把两次不同
的生成拼接成一条前后矛盾的回复。

- generate_stream: 一旦有部分内容已输出,跳过重试并干净结束流,客户端收到
  格式完整(finish_reason + [DONE])的响应,可自行请求续写
- _get_httpx_client: 禁用 keep-alive 连接复用(Connection: close),避免
  复用已被代理/上游关闭的连接导致流中途 SSL 断裂

现有 18 个测试全部通过。
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.

1 participant