保真模式下流以 finish_reason+usage 正常结束但缺 [DONE],被判失败并重试到耗尽(ClinDB-ReviewBench、dissect3) #31
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
背景
版本:polygateway 1.3.9。ClinDB-ReviewBench 与 dissect3 都用
chat(..., response_mode="openai")流式调用(pi 经本地代理,见 #29)。与 #30 同一类:保真模式把上游的正常结局当成失败去重试;本条是另一种情形。现象
经一个 OpenAI 兼容中转站(magpie,模型名
group/auto-glm-5-3-flash,上游 glm-5.3-flash)流式调用时,上游经常这样结束:choices[0].finish_reason为stop/length/tool_calls,同一块带完整usage;data: [DONE]。此时
transports/openai_compat.py:668-671抛TransientError("保真响应缺 [DONE],禁止打捞")。这条判断不看SourceConfig.missing_done;错误可重试,于是重拨到max_attempts耗尽,最后报AllSourcesExhausted ... retry_exhausted。证据(两个项目独立观察)
finish_reason为stop和length时都没有[DONE](十来次,一次都没有);经 PolyGateway 的 glm 调用全部失败,一次在max_attempts=10下挂了 1216 秒。同一中转站上 minimax 的回复带[DONE],正常。max_tokens: 50、include_usage):HTTP 200,末块为{"choices":[{"index":0,"finish_reason":"length","delta":{...}}],"usage":{...}},随后断开,无[DONE]。所以这个上游有时发
[DONE]、有时不发;不发的那些在 PolyGateway 里一律失败。为什么算缺陷
internal/gateway/gateway.go:4238:「A Chat stream may end without [DONE] and be whole, so only a read that failed counts」);OpenAI 官方 SDK 也以连接结束作为流结束。[DONE],重拨后很可能还是不发;每次重拨都生成一份完整回复、照常计费、又被丢掉,而调用方因为最终判失败,这些用量一条都记不到。missing_done在 openai 模式下不生效。建议的修法
openai 模式下,没有收到
[DONE]时,同时满足以下三点即判为完整、正常交回:finish_reason;usage(PolyGateway 已强制stream_options.include_usage,openai_compat.py:423,所以总能检查);以下维持现状、仍判失败,不放宽「禁止打捞」:没有
finish_reason就断;有finish_reason无usage;读取出错;收到 error 帧。建议在结果里标出结束方式,例如
stream_end: "done" | "eof_after_finish",让调用方能记录和统计,不悄悄混在一起。验收(假 SSE 服务即可复现,不花钱)
finish_reason: "stop"+usage,然后关闭,无[DONE]:成功,completion完整,用量按实测,stream_end="eof_after_finish",attempts=1。finish_reason: "tool_calls",带完整工具调用:成功,工具调用完整。finish_reason即关闭:仍失败(与现状同)。finish_reason、无usage即关闭:失败。RemoteProtocolError等):失败。[DONE]的正常流:行为不变,stream_end="done"。已在 1.3.10 修复并发布(registry 可安装:
polygateway==1.3.10;Release:https://gitea.iomgaa.online/iomgaa/PolyGateway/releases/tag/v1.3.10)。与 #30 同根,统一修复。修法:流完整性改以上游声明为准——收到
[DONE]→stream_end="done";未发[DONE]但 finish_reason 已到、其同帧或之后收到的 usage 为实测、连接干净关闭 → 完整,stream_end="eof_after_finish",用量记 measured。两种模式都修(文本模式默认MISSING_DONE=retry原本同样重试到耗尽);missing_done只管真截断。读取异常/活性超时/error 帧照旧失败;finish 为network_error/error仍判瞬时。LLMResponse.stream_end与遥测stream_end列可统计结束方式。验收(假 SSE,先红后绿):issue 6 条全部覆盖(
tests/unit/test_completion_transport.py),另加"usage 早于 finish""finish 后 usage 为 {}""network_error"及经 GatewayClient 的 attempts=1。全套件 2000 passed(main 合并后)。真实中转对照(magpie / group/auto-glm-5-3-flash,2026-10-15):
[DONE],finish 后带 usageAllSourcesExhausted retry_exhausted(最长 220s)eof_after_finish,measuredAllSourcesExhaustedeof_after_finish(1.3.10 有 2 次 attempts=2 是首次尝试撞
TIMEOUT_S=180的上游卡顿,同期直连亦有一次 ReadTimeout,属正常超时重试。)设计
research-wiki/designs/2026-10-14-issue30-31-completion-verdict-design.md。