fix(web): keep webapp generation running when switching conversations - #41227
Draft
GareArc wants to merge 3 commits into
Draft
fix(web): keep webapp generation running when switching conversations#41227GareArc wants to merge 3 commits into
GareArc wants to merge 3 commits into
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #41227 +/- ##
==========================================
- Coverage 86.82% 86.71% -0.11%
==========================================
Files 5266 5431 +165
Lines 302135 307120 +4985
Branches 60485 61751 +1266
==========================================
+ Hits 262314 266323 +4009
- Misses 34591 35557 +966
- Partials 5230 5240 +10
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Switching to another conversation while a message was still streaming
fired POST /chat-messages/{task_id}/stop, cancelling the run server-side
and leaving a truncated answer persisted as a normal, error-free message.
Streaming execution now runs in a Celery worker and the HTTP request is
only one subscriber on the run's Redis topic, so dropping the response no
longer cancels anything - but the explicit stop call still does. Wiring
navigation to the same handler as the stop button therefore turned a
"stop listening" action into a "stop generating" one.
Split useChat's handleStop in two: handleDetach releases this client's
subscriptions, and handleStop additionally cancels the run. Switching
conversations and starting a new one now detach; the stop button and
deleting the conversation a run belongs to still cancel it.
handleDetach also clears taskIdRef, so a detached run can no longer be
cancelled through a stale chat instance reference after the wrapper has
remounted for another conversation.
GareArc
force-pushed
the
fix/webapp-no-stop-on-conversation-switch
branch
from
August 25, 2026 05:31
297914a to
99126ca
Compare
GareArc
marked this pull request as ready for review
August 25, 2026 05:48
GareArc
requested review from
CodingOnStar,
hyoban,
iamjoel and
zxhlyh
as code owners
August 25, 2026 05:48
GareArc
marked this pull request as draft
August 25, 2026 05:54
A run the user cancelled and one the watchdog aborted on timeout were both persisted as ordinary, error-free messages, so the logs reported them as successes and the webapp gave no sign the answer was cut short. The abort cause was lost twice. workflow_app_runner hardcoded USER_MANUAL for every GraphRunAbortedEvent, and the message row was never moved off `normal` on the stop path - only the workflow run picked up STOPPED. QueueStopEvent.StopBy now owns its reason text and recovers a member from the free-form reason that crosses the graph engine boundary, degrading to UNKNOWN for a value this version has never heard of and passing the engine's own text through untouched. The coordinator and AppTaskService send that typed cause, so a timeout is no longer reported as a manual stop. MessageStatus gains STOPPED, which the advanced chat pipeline persists. Both log surfaces read it: the console conversation list gained a stopped count, and the agent observability logs report and filter on it. A failure still outranks an interruption in the same conversation. MessageListItem.status and StatusCount.stopped are typed on the API rather than left as bare str, so the generated contract carries the enum and the web drops its two hand-written mirrors. The webapp narrows the served status at the boundary and renders nothing for one it cannot recognise.
Contributor
Pyrefly Type Coverage
|
Contributor
Pyrefly Diffbase → PR--- /tmp/pyrefly_base.txt 2026-08-25 09:44:29.198699384 +0000
+++ /tmp/pyrefly_pr.txt 2026-08-25 09:44:21.456660500 +0000
@@ -5368,8 +5368,6 @@
--> tests/unit_tests/core/app/apps/workflow/test_generate_task_pipeline_core.py:880:60
ERROR `(**kwargs: Unknown) -> TestWorkflowGenerateTaskPipeline.test_save_output_for_event_writes_draft_variables._Saver` is not assignable to attribute `_draft_var_saver_factory` with type `DraftVariableSaverFactory` [bad-assignment]
--> tests/unit_tests/core/app/apps/workflow/test_generate_task_pipeline_core.py:921:45
-ERROR `Literal['unknown']` is not assignable to attribute `stopped_by` with type `QueueStopEvent.StopBy` [bad-assignment]
- --> tests/unit_tests/core/app/entities/test_queue_entities.py:18:28
ERROR Cannot index into `bool` [bad-index]
--> tests/unit_tests/core/app/entities/test_task_entities.py:32:16
ERROR Cannot index into `float` [bad-index]
@@ -8318,12 +8316,14 @@
--> tests/unit_tests/services/agent/test_agent_dsl_service.py:578:17
ERROR Argument `SimpleNamespace` is not assignable to parameter `workflow` with type `Workflow` in function `services.agent.dsl_service.AgentDslService._create_workflow_only_agent` [bad-argument-type]
--> tests/unit_tests/services/agent/test_agent_dsl_service.py:601:18
+ERROR Argument `SimpleNamespace` is not assignable to parameter `message` with type `Message` in function `services.agent.observability_service.AgentObservabilityService._message_status` [bad-argument-type]
+ --> tests/unit_tests/services/agent/test_agent_observability_service.py:262:54
ERROR Argument `SimpleNamespace` is not assignable to parameter `message` with type `Message` in function `services.agent.observability_service.AgentObservabilityService.serialize_log_message` [bad-argument-type]
- --> tests/unit_tests/services/agent/test_agent_observability_service.py:630:9
+ --> tests/unit_tests/services/agent/test_agent_observability_service.py:638:9
ERROR Argument `SimpleNamespace` is not assignable to parameter `conversation` with type `Conversation | None` in function `services.agent.observability_service.AgentObservabilityService.serialize_log_message` [bad-argument-type]
- --> tests/unit_tests/services/agent/test_agent_observability_service.py:631:9
+ --> tests/unit_tests/services/agent/test_agent_observability_service.py:639:9
ERROR Argument `list[SimpleNamespace]` is not assignable to parameter `feedbacks` with type `Sequence[MessageFeedback]` in function `services.agent.observability_service.AgentObservabilityService.serialize_log_message` [bad-argument-type]
- --> tests/unit_tests/services/agent/test_agent_observability_service.py:632:9
+ --> tests/unit_tests/services/agent/test_agent_observability_service.py:640:9
ERROR Argument `SimpleNamespace` is not assignable to parameter `binding` with type `WorkflowAgentNodeBinding` in function `services.agent.composer_service.AgentComposerService._workflow_inline_debug_conversation_id` [bad-argument-type]
--> tests/unit_tests/services/agent/test_agent_services.py:400:17
ERROR Argument `SimpleNamespace` is not assignable to parameter `agent` with type `Agent | None` in function `services.agent.composer_service.AgentComposerService._workflow_inline_debug_conversation_id` [bad-argument-type]
|
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.
Summary
Sending a long message in a WebApp Advanced Chat and then switching to another conversation while it is still streaming truncates the answer. The message is persisted with
status=normalanderror=None, so it looks like a complete reply rather than a cancelled one.The frontend switches conversations fires
POST /chat-messages/{task_id}/stop.web/app/components/base/chat/chat/hooks.tsexposed a singlehandleStop()that did two unrelated things — released this client's subscriptions (aborting the SSE POST, the conversation-messages and suggested-questions fetches, resetting the workflow-events subscription), and cancelled the run server-side. Both conversation-navigation paths inchat-with-historyandembedded-chatbotcalled it.That coupling was harmless when generation ran inside the HTTP request, because disconnecting was equivalent to cancelling. It is not harmless now: streaming Advanced Chat executes in a Celery worker and the HTTP request is only one subscriber on the run's Redis topic. Dropping the response no longer cancels anything, but the explicit stop call still does — so navigation became the one action that kills a run implicitly, which is the opposite of what the backend does everywhere else (
AppExecutionCoordinator: "Response detachment is not an execution cancellation signal").Checklist
make lint && make type-check(backend) andvp staged(frontend) to appease the lint godsFrontend only, so the backend gates do not apply.
vp stagedhas no config in this repo;vp checkandtscboth pass on the changed files. Tests: 1441 passing across 78 files inapp/components/base/chatand__tests__/base.Three tests cover this change, deliberately kept narrow:
useChatdetaches without cancelling the run, and a detached run can no longer be cancelled afterwards.Notes for reviewers
handleStopis now reached from exactly two places: the stop button, and deleting the conversation a run belongs to. Everything else detaches.chat-with-history/chat-wrapper.tsxgained a real dependency array (it was[]). The fix depends on the registered callbacks being current, and this matches whatembedded-chatbotalready did.