fix(webchat): preserve coalesced replies and retry failed turns - #6
Conversation
Split from #7#7 dropped the two WebChat commits that duplicated this branch ( This PR remains the sole owner of WebChat turn-handling. Merge with #7 in either order; no shared files remain. I will not merge. |
Clone the last event on flush so WebChat retains queue-manager state, and skip empty completion on buffered fragments so WebChat does not emit end before the flush replies. AI-Generated: true Generated-At: 2026-09-01T22:01:31Z
Retry a failed WebChat turn from platform history when the conversation checkpoint is missing, instead of rejecting the request. AI-Generated: true Generated-At: 2026-09-01T22:01:31Z
Keep editing a failed WebChat turn and creating a thread when the conversation checkpoint is missing, instead of rejecting the request. AI-Generated: true Generated-At: 2026-09-01T22:05:12Z
Add the fork WebChat turn-handling fixes to the 4.27.6 notes after rebasing onto master. AI-Generated: true Generated-At: 2026-09-02T18:24:37Z
e428551 to
cc0a161
Compare
BegoniaHe
left a comment
There was a problem hiding this comment.
AI-assisted review (not a maintainer approval)
Separate from the authoring Agent note. GitHub rejects REQUEST_CHANGES on a self-authored PR, so this is a comment review. I will not merge.
Reviewed head cc0a16183 after rebase onto master (9a73e897e, #7). File overlap with the absorbed sync is gone. Focused pytest 79 passed in this session.
Verdict
Technically mergeable once CI on this head is green and a human maintainer approves. No blocking product defect found.
What looks correct
- Coalesced fragments set
skip_empty_completionandstop_event(). Scheduler skipssend(None), so WebChat does not emitendbefore flush. - Flush uses
copy.copy(last)then resets_extras, result, send flags, and temp-file/task sets.WebChatMessageEventkeepswebchat_queue_manager/attachments_dir. Flush does not inherit the skip extra. #9653empty Respond skip is a different path (empty non-streaming chains). Fragments never reach Respond; flush carries merged content. They compose.- Regenerating/editing/threading a failed turn without
find_turn_rangecontinues from platform history. When a range exists, older-turn branching checks still run. - Tests cover skip-empty-completion, WebChat flush clone, and the three missing-checkpoint recovery paths.
Residual (non-blocking)
copy.copyis shallow. WebChat needed the extra instance fields; other adapters now keep extra fields too.- Missing
turn_rangeskips conversation-history rewrite and older-turn checks. Intended for the last failed turn. An older failed bot message in a session that later continued can regenerate/edit/thread without the latest-turn guard. - Changelog records the user-visible fix. WebUI docs already mention regenerate; no API/docs contract change.
Human maintainer still has to approve. This review does not authorize merge.
Summary
WebChat coalesced fragments were finishing the pipeline with an empty completion (
end) before the later flush event replied. Flush also reconstructed events through the four-argument constructor, soWebChatMessageEventlost its queue manager and attachments directory.Failed turns that wrote platform history but never a conversation checkpoint also could not be regenerated, edited, or used as a thread parent.
This keeps fragment events from emitting empty completion, clones the last event on flush, and lets regenerate/edit/thread continue from platform history when the conversation checkpoint is missing.
Rebased onto
masterafter #7. No shared files remain.Related issue
Related: #7
Root cause
PipelineScheduler.executealways sent empty completion forrequires_empty_completionafter stages.TurnCoalesceStagestops the fragment and defers work to a later flush, but the stopped fragment still emitted WebChatend._build_flush_eventcalledlast.__class__(message_str, message_obj, platform_meta, session_id).WebChatMessageEvent.__init__also requireswebchat_queue_managerandattachments_dir, so the flush event could not emit replies.ChatServiceregenerate, edit, and thread creation requiredfind_turn_rangeto match a conversation checkpoint. A failed AI turn can have platform history plusllm_checkpoint_idwithout a matching conversation-history range.Reproduction
endbefore the flush reply.Linked checkpoint not found.Implementation notes
skip_empty_completionand honor it in the scheduler.copy.copy(last)so adapter-specific fields stay attached, then reset per-run extras, result, and send flags.turn_rangeis missing, skip conversation-history rewrite and continue from platform history for regenerate, edit, and thread creation. Older-turn branching checks still apply when a checkpoint range exists.changelogs/v4.27.6.md.Validation
79 passed after rebase onto
master. Did not runmake checkor the full pytest suite.Compatibility and risk
No OpenAPI, route, or public plugin-API change.
skip_empty_completionis an event-local extra. Residual risk: other adapters that depended on constructor-only flush reconstruction now keep extra instance fields viacopy.copy; WebChat is the adapter that required that. Failed-turn recovery does not rewrite conversation history when no checkpoint range exists. Older-turn branching checks are skipped whenturn_rangeis missing.Checklist
docs/zh/anddocs/en/when needed.docs/public/openapi.json, and tests change together when routes or schemas change.pyproject.toml,requirements.txt, anduv.locktogether.!and aBREAKING CHANGE:footer.Agent note
Goal: rebase #6 onto
masterafter #7 so GitHub merge is clean, and record the WebChat fixes inchangelogs/v4.27.6.md.Paths:
astrbot/core/pipeline/scheduler.py,turn_coalesce/stage.py,turn_window.py,astrbot/dashboard/services/chat_service.py, unit tests,changelogs/v4.27.6.md.Checks run: Ruff format/check on the seven product files;
uv run pyteston scheduler/coalesce/chat_service/delivery_receipt, 79 passed. Residual risk: fullmake check/make testnot run here; failed-turn paths without a conversation checkpoint leave conversation history untouched.Tools: grok-4.6 in opencode.