fix(stream): preserve Chatflow terminal events and completion - #165
Open
SamizuHM wants to merge 1 commit into
Open
fix(stream): preserve Chatflow terminal events and completion#165SamizuHM wants to merge 1 commit into
SamizuHM wants to merge 1 commit into
Conversation
SamizuHM
force-pushed
the
fix/chatflow-stream-completion
branch
from
July 30, 2026 18:46
46a6ff8 to
9c76fa8
Compare
SamizuHM
force-pushed
the
fix/chatflow-stream-completion
branch
from
July 30, 2026 20:07
86fc23c to
4f3beb0
Compare
SamizuHM
marked this pull request as ready for review
July 31, 2026 05:23
Copilot stopped reviewing on behalf of
SamizuHM due to an error
July 31, 2026 05:23
Copilot stopped reviewing on behalf of
SamizuHM due to an error
July 31, 2026 05:24
SamizuHM
marked this pull request as draft
July 31, 2026 05:37
SamizuHM
force-pushed
the
fix/chatflow-stream-completion
branch
from
July 31, 2026 08:45
4f3beb0 to
00d825d
Compare
SamizuHM
marked this pull request as ready for review
August 11, 2026 20:04
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Chatflow streams end with two terminal events,
message_endandworkflow_finished. Dify's streaming guide says they arrive in that order:The current SDK treats the first terminal event as the end of the stream: it dispatches the event, then breaks out of the read loop and discards the rest of the SSE response. In the documented order that is harmless, because
workflow_finishedreally is the last event. In real deployments the order is not guaranteed. When a Dify server sendsworkflow_finishedfirst andmessage_endsecond, the SDK stops atworkflow_finishedand silently dropsmessage_end.Chatflow 流以
message_end和workflow_finished两个终止事件结尾。Dify 官方文档定义它们的到达顺序为:先message_end,后workflow_finished。当前 SDK 把遇到的第一个终止事件当作流的终点:分发该事件后立刻跳出读取循环,丢弃 SSE 响应中剩余的内容。在文档顺序下这没有问题,因为
workflow_finished确实是最后一个事件;但真实部署不保证顺序。当 Dify 服务器先发workflow_finished、再发message_end时,SDK 会在workflow_finished处停止读取,message_end被静默丢弃。Why the code ended up this way
This is not a freshly invented design decision; it reflects three rounds of changes in this repo, none of which solved the underlying order problem:
workflow_finishedlosesmessage_endin some deployments, and removedworkflow_finishedfrom the terminal set so reading continues untilmessage_end.WORKFLOW_TERMINAL_EVENTS(stop atworkflow_finished), which restored the documented-order behavior but re-introduced the loss ofmessage_endin reverse-order deployments.Each iteration chose one event as the single boundary, so each iteration is wrong for the other order. The two events are both terminal and their order is environmental, not contractual. Building the read loop around “stop at the first terminal event” cannot work for both orderings.
现状不是一次性的设计决策,而是本仓库三轮改动叠加的结果,没有一轮真正解决顺序问题:
workflow_finished停止会丢失message_end,于是把workflow_finished从终止事件集合中移除,继续读到message_end。WORKFLOW_TERMINAL_EVENTS(在workflow_finished停止),恢复了文档顺序下的行为,但重新引入了反序部署下message_end丢失的问题。每一轮都只挑一个事件作为边界,所以每一轮在另一种顺序下都是错的。这两个事件都是终止事件,而它们的顺序取决于环境、不构成协议契约。把读取循环建立在“遇到第一个终止事件就停”之上,无法同时兼容两种顺序。
Real-stream observation
A probe against a real Chatflow produced 343 events ending with:
The reverse of the documented order. With the current code, event 343 is never read.
对真实 Chatflow 做了一次探针,收到 343 个事件,结尾为:
与文档顺序相反。在当前代码下,第 343 个事件永远不会被读取。
Solution
Make the Chatflow read loop order-independent: track
message_endandworkflow_finishedper stream, and stop reading only after both have been dispatched. If the connection reaches EOF first, EOF ends the read normally.Separate error handling from normal completion: a Dify
errorevent or transport failure ends the stream throughonError/onExceptionand does not invoke the normal completion callback.Wire the normal completion callback into the other streaming entry points (chat, completion, workflow, workflow events, knowledge pipeline) so all streams share the same lifecycle contract.
让 Chatflow 读取循环与事件顺序无关:按流分别跟踪
message_end与workflow_finished,两个事件都分发后才停止读取;若连接先到达 EOF,则以 EOF 正常结束读取。把错误处理与正常完成分开:Dify
error事件或传输层异常通过onError/onException结束流,不触发正常完成回调。把正常完成回调接入其余流式入口(chat、completion、workflow、workflow events、knowledge pipeline),让所有流共享同一生命周期契约。
Behavior after this change
erroreventonErroronExceptiononStreamCompletemeans the SDK has finished reading a non-error response; it does not assert that the business operation succeeded.error事件onErroronExceptiononStreamComplete表示 SDK 已读完非错误响应,不代表业务操作成功。Tests
In-memory SSE tests cover documented order, reverse order, EOF before both terminal events, error events not triggering completion, and completion on the remaining streaming entry points. All pass with
mvn -Dtest=ChatflowStreamTerminalEventTest,PipelineStreamCompletionTest,WorkflowEventsStreamCompletionTest test.内存 SSE 测试覆盖文档顺序、反序、两个终止事件到达前 EOF、error 事件不触发完成,以及其余流式入口的正常完成回调。
mvn -Dtest=ChatflowStreamTerminalEventTest,PipelineStreamCompletionTest,WorkflowEventsStreamCompletionTest test全部通过。