fix: clarify cancelled chat turn status - #2151
2 commits merged into
Conversation
|
Reading the diff for Classification helper (
|
Review Feedback Addressed
🤖 AI-assisted via Hermes Agent |
- classify string-only CancelledError payloads as cancelled - centralize cancel marker substring matching - add targeted regression coverage
|
Addressed the review feedback in follow-up commit
|
|
This closure was accidental. The branch has been restored and PR #2151 has been reopened so review can continue. |
6aedb7e
fix: clarify cancelled chat turn status (Jordan-SkyLF) Conflict resolution on api/streaming.py:4549-4567 (the cancel-handler ownership guard). Both this PR and the already-shipped PR nesquena#2136 add a guard at the same site against stale stream writebacks, from different angles: - PR nesquena#2136 (HEAD): _stream_writeback_is_current(_cs, stream_id) — strictly dominates by checking the active_stream_id token equality. - PR nesquena#2151: 'worker won the race' check via (active_stream_id != stream_id and not pending_user_message), with _emit_cancel_event = False to suppress the terminal cancel event. Resolution merges both: keep nesquena#2136's strictly-stronger condition for skip detection, and adopt nesquena#2151's _emit_cancel_event = False semantic so the cancel event isn't emitted in addition to skipping the writeback (when client may have already received the successful done payload). 55/55 tests pass across cancelled-turn-status + stale-stream-writeback + the four cancel/data-loss sibling test files.
…edup scope Opus flagged that PR nesquena#2151's cancel-handler partial-dedup loop used a substring check that was too broad: any short prior assistant reply ('OK', 'Here is the answer:') would dedup a longer new partial containing it, silently dropping the partial and resurrecting the nesquena#893 data-loss bug. Tightened to only dedup against actual prior _partial=True markers with exact (whitespace-stripped) content match. Three new regression tests added (short-non-partial-prefix-does-not-dedup, exact-partial-match-still- dedups, same-content-non-partial-does-not-dedup). 10/10 partial-cancel tests pass after the fix. Also updated CHANGELOG with the conflict-resolution notes for nesquena#2151 vs nesquena#2136 and the nesquena#2178 test-fix.
fix: clarify cancelled chat turn status (Jordan-SkyLF) Conflict resolution on api/streaming.py:4549-4567 (the cancel-handler ownership guard). Both this PR and the already-shipped PR nesquena#2136 add a guard at the same site against stale stream writebacks, from different angles: - PR nesquena#2136 (HEAD): _stream_writeback_is_current(_cs, stream_id) — strictly dominates by checking the active_stream_id token equality. - PR nesquena#2151: 'worker won the race' check via (active_stream_id != stream_id and not pending_user_message), with _emit_cancel_event = False to suppress the terminal cancel event. Resolution merges both: keep nesquena#2136's strictly-stronger condition for skip detection, and adopt nesquena#2151's _emit_cancel_event = False semantic so the cancel event isn't emitted in addition to skipping the writeback (when client may have already received the successful done payload). 55/55 tests pass across cancelled-turn-status + stale-stream-writeback + the four cancel/data-loss sibling test files.
…edup scope Opus flagged that PR nesquena#2151's cancel-handler partial-dedup loop used a substring check that was too broad: any short prior assistant reply ('OK', 'Here is the answer:') would dedup a longer new partial containing it, silently dropping the partial and resurrecting the nesquena#893 data-loss bug. Tightened to only dedup against actual prior _partial=True markers with exact (whitespace-stripped) content match. Three new regression tests added (short-non-partial-prefix-does-not-dedup, exact-partial-match-still- dedups, same-content-non-partial-does-not-dedup). 10/10 partial-cancel tests pass after the fix. Also updated CHANGELOG with the conflict-resolution notes for nesquena#2151 vs nesquena#2136 and the nesquena#2178 test-fix.
stage-350: medium-risk batch — auth trilogy (nesquena#2191/2/3) + cancel-status nesquena#2151 with conflict resolution + nesquena#2178 ollama guard + nesquena#2204 provider precedence + nesquena#2203 activity animation
fix: clarify cancelled chat turn status (Jordan-SkyLF) Conflict resolution on api/streaming.py:4549-4567 (the cancel-handler ownership guard). Both this PR and the already-shipped PR nesquena#2136 add a guard at the same site against stale stream writebacks, from different angles: - PR nesquena#2136 (HEAD): _stream_writeback_is_current(_cs, stream_id) — strictly dominates by checking the active_stream_id token equality. - PR nesquena#2151: 'worker won the race' check via (active_stream_id != stream_id and not pending_user_message), with _emit_cancel_event = False to suppress the terminal cancel event. Resolution merges both: keep nesquena#2136's strictly-stronger condition for skip detection, and adopt nesquena#2151's _emit_cancel_event = False semantic so the cancel event isn't emitted in addition to skipping the writeback (when client may have already received the successful done payload). 55/55 tests pass across cancelled-turn-status + stale-stream-writeback + the four cancel/data-loss sibling test files.
…edup scope Opus flagged that PR nesquena#2151's cancel-handler partial-dedup loop used a substring check that was too broad: any short prior assistant reply ('OK', 'Here is the answer:') would dedup a longer new partial containing it, silently dropping the partial and resurrecting the nesquena#893 data-loss bug. Tightened to only dedup against actual prior _partial=True markers with exact (whitespace-stripped) content match. Three new regression tests added (short-non-partial-prefix-does-not-dedup, exact-partial-match-still- dedups, same-content-non-partial-does-not-dedup). 10/10 partial-cancel tests pass after the fix. Also updated CHANGELOG with the conflict-resolution notes for nesquena#2151 vs nesquena#2136 and the nesquena#2178 test-fix.
stage-350: medium-risk batch — auth trilogy (nesquena#2191/2/3) + cancel-status nesquena#2151 with conflict resolution + nesquena#2178 ollama guard + nesquena#2204 provider precedence + nesquena#2203 activity animation
Thinking Path
Small, focused bug fix. Explicit user cancellation should not be reported as a provider/no-content failure, and the UI should keep the same verbose error-card shape users already see for real provider errors.
What Changed
_errorassistant markers with verbose copy and aCancellation detailsdisclosure, so reloads match the live UI./api/chat/canceldo not duplicate cancel markers,_errorsanitization path.Why It Matters
Previously, cancelled or interrupted turns could look like provider failures, especially the misleading
No response from providerpath. That makes normal user cancellation look like an upstream/model problem and can persist incorrect state across reloads.This keeps the user-visible format familiar while making the status accurate:
Task cancelled+Cancellation detailsResponse interrupted+Interruption detailsNo response from provider+Provider detailsVerification
git diff --checkpython -m py_compile api/streaming.pynode --check static/messages.jsnode --check static/ui.jspython -m pytest tests/test_issue1361_cancel_data_loss.py tests/test_cancelled_turn_status.py tests/test_issue893_cancel_preserves_partial.py -q33 passedpython -m pytest tests/ -q --tb=short5322 passed, 4 skipped, 1 xfailed, 2 xpassed, 8 subtests passed in 154.90sTask cancelled:output,Cancellation details,No response from provider,{}.<CancelledError>classification coverage,_CANCEL_MARKER_PATTERNS,112eadc.Screenshots / UI Notes
This is a small copy/status/details-label change that intentionally reuses the existing provider-error UI. Manual browser smoke verified the after-state; no new interface component or layout was introduced.
Risks / Follow-ups
api/streaming.pyis later refactored.Model Used
AI-assisted with Hermes/Skyly using OpenAI
gpt-5.5. An independent AI review pass was also run before opening the PR; it reported no blocking issues or security concerns.