fix(chat): preserve reasoning across streaming transitions (#709)
Flush throttled reasoning before state-transition events (MessageStart/ Complete/Done/ToolStart) so a reasoning delta arriving just before a transition is not dropped. Also compare reasoningText in sameMessages so a reasoning-only restore actually re-merges. - ChatStreamingController.flushPendingReasoning() forces a synchronous flush - handleWsEvent calls it for the transition events - sameMessages now includes reasoningText in its equality check - tests: ChatStreamingControllerTest (flush + no-resurrect), reasoning survives MessageDone after an unflushed delta Co-authored-by: M57 <hy4ri@users.noreply.github.com>
This commit is contained in:
1 parent
223f9b676c
commit
4e2fb05077
3 files changed
+104
-1
No files matched your search
@@ -56,6 +56,17 @@ class ChatStreamingController(
|
||||
resetStreaming()
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes any throttled reasoning buffer before a state-transition event
|
||||
* (MessageStart / MessageComplete / MessageDone / ToolStart). Without this,
|
||||
* a reasoning delta that arrived just before a transition could be dropped
|
||||
* because the next flush only fires on the ~33ms timer — so we force it out
|
||||
* synchronously so the finalized message carries the latest reasoning.
|
||||
*/
|
||||
fun flushPendingReasoning() {
|
||||
flushReasoning()
|
||||
}
|
||||
|
||||
fun handleMessageToken(event: WsEvent.MessageToken) {
|
||||
if (!isCurrentSession(event.sessionId)) return
|
||||
|
||||
|
||||
@@ -341,6 +341,18 @@ class ChatViewModel(
|
||||
}
|
||||
|
||||
private fun handleWsEvent(event: WsEvent) {
|
||||
// Flush any throttled reasoning before a state transition so the
|
||||
// finalized/orphan message carries the latest reasoning text.
|
||||
when (event) {
|
||||
is WsEvent.MessageStart,
|
||||
is WsEvent.MessageComplete,
|
||||
is WsEvent.MessageDone,
|
||||
is WsEvent.ToolStart,
|
||||
-> streamingController.flushPendingReasoning()
|
||||
|
||||
else -> Unit
|
||||
}
|
||||
|
||||
// First, let the reducer compute the new state and any effects
|
||||
val result =
|
||||
ChatWsEventReducer.reduce(
|
||||
@@ -1627,7 +1639,10 @@ class ChatViewModel(
|
||||
): Boolean =
|
||||
left.size == right.size &&
|
||||
left.zip(right).all { (a, b) ->
|
||||
a.id == b.id && a.role == b.role && a.content == b.content
|
||||
a.id == b.id &&
|
||||
a.role == b.role &&
|
||||
a.content == b.content &&
|
||||
a.reasoningText == b.reasoningText
|
||||
}
|
||||
|
||||
// ── UI actions ───────────────────────────────────────────────────────
|
||||
|
||||
Reference in new issue
Block a user