[Feature Request] Add cancellation API for streaming callbacks
Les mainteneurs répondent en général sous 2 jours
Personne n'a encore pris cette issue.
Évaluation
- Difficulté
- 5/5
- Temps estimé
- Plus d'une semaine
- Accessibilité débutants
- 45/100
- Type d'issue
- Fonctionnalité
- Clarté
- Plutôt claire
- Activité
- Active
- Stack technique
- javascript, python
- Domaine
- backend-api-design, frontend
Piste de recherche
Read the streaming callback implementation introduced in #3931 and the existing SharedWorker/page-close cancellation path. Confirm cancellation behavior for standalone NDJSON, multiplexed transport, async-generator cleanup, running outputs, and concurrent invocations; done means cancel=[Input(...)] works without application polling.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Description
Thanks so much for your interest in Dash!
Before posting an issue here, please check the Dash community forum to see if the topic has already been discussed. The community forum is also great for implementation questions. When in doubt, please feel free to just post the issue here :)
Is your feature request related to a problem? Please describe.
Dash 4.5 streaming callbacks allow an async def generator callback to stream yielded values to the browser, including incremental Patch updates. However, an application cannot currently provide a reliable stop button for an ordinary streaming callback through the public callback API.
running can keep start/stop controls in the appropriate loading and disabled states, but it does not cancel the active generator. Triggering the callback again also does not reliably stop the previous invocation. The existing cancel=[Input(...)] callback argument is limited to background callbacks.
The transport already has cancellation primitives, but they are not exposed to applications:
- With multiplexed streaming, the renderer tracks a per-stream request ID and can send
streamCancel; this is already used when a tab is closed. - Without shared storage, each stream falls back to its own NDJSON HTTP response. Aborting that request can close the response iterator and cancel the async generator, but application code has no public handle for the request or its
AbortController.
As a result, applications must build a separate cooperative cancellation protocol, often involving ctx.shared_storage, unique stream IDs, polling a cancellation flag before every yield, and manual cleanup. This is much more application code than should be necessary for a fundamental streaming operation.
Describe the solution you'd like
Please support cancel=[Input(...)] for ordinary streaming callbacks, with semantics similar to background callback cancellation:
from dash import Input, Output, Patch, callback
@callback(
Output("out", "children"),
Input("start", "n_clicks"),
cancel=[Input("stop", "n_clicks")],
running=[
(Output("start", "loading"), True, False),
(Output("start", "disabled"), True, False),
(Output("stop", "disabled"), False, True),
],
prevent_initial_call=True,
)
async def stream(_):
async for token in llm.stream(prompt):
patch = Patch()
patch += token
yield patch
Ideally, Dash would implement cancellation according to the active transport:
- Standalone NDJSON: abort the callback fetch request with its
AbortController. - Multiplexed SharedWorker transport: send
streamCancelfor the active request ID without closing the shared downlink. - Server: cancel/close the active async generator, run its
finallycleanup, restore allrunningoutputs, and treat cancellation as expected control flow rather than a callback error.
This should work without requiring application-level polling or direct use of shared storage. If multiple invocations are permitted concurrently, the API should define whether the cancel input cancels all active invocations for that callback or only the invocation associated with the current output grouping.
Describe alternatives you've considered
- Store cancellation flags in
ctx.shared_storageand check them before every yielded item. This works across workers but requires stream IDs, polling, cleanup, and storage configuration in application code. - Use an in-process
dict[str, asyncio.Event]. This works only in a single-process deployment and fails when the start and stop requests reach different workers. - Build a custom clientside component that retains the request's
AbortController. This depends on renderer internals and duplicates transport logic. - Use a background callback only to obtain
cancel=[Input(...)]. Background callbacks do not provide the same incremental streaming behavior. - Replace the native callback with EventSource or Socket.IO and implement a separate cancellation endpoint. This gives up the simplicity of native Dash streaming callbacks.
Additional context
Streaming callbacks were introduced in #3931. The existing SharedWorker/page-close cancellation path demonstrates that Dash already has most of the transport and server-side cancellation machinery. Exposing it through the callback API would make native streaming callbacks suitable for common LLM interfaces with explicit Start generating and Stop generating controls, while preserving the single shared browser connection used by multiplexed streams.
- Langage dominant
- Python
- Étoiles
- 24.4k
- Forks
- 2.3k
- Merge moyen
- 2 j 14 h
- PR mergées (30 j)
- 23
Préparer son environnement
- Aucun Dockerfile ni fichier Docker Compose
- Propose un modèle de pull request
- Lire le guide de contribution
Par où commencer
- Lisez l'issue en entier, puis le guide de contribution du projet.
- Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
- Forkez le dépôt et travaillez sur une branche.
- Ouvrez une pull request qui référence le numéro de l'issue.
Autres issues de plotly/dash
-
P2 plotly-internal size: 1 task
Difficulté 2/5 1-3 heures Accessibilité débutants 78/100
Les mainteneurs répondent en général sous 2 jours
-
Extract graph schema validation into properly named testPeut-être pris @Nice6042 l’a pris il y a 58 jours. Ouvertegood first issue P3 size: 1 task
Difficulté 2/5 1-3 heures Accessibilité débutants 68/100
plotly/dash#3735 · 3 commentaires ·
Les mainteneurs répondent en général sous 2 jours
-
[BUG] dcc.Dropdown: hidden focus-target input is focusable inside aria-hidden and can overflow its wrapperPeut-être pris @KoolADE85 l’a pris il y a 6 jours. Ouvertebug P2 size: 1
Difficulté 4/5 3-5 jours Accessibilité débutants 56/100
plotly/dash#4042 · 3 commentaires · 1 personne assignée ·
Les mainteneurs répondent en général sous 2 jours
-
Linter/Type Checking ReplacementOuverteP3 plotly-internal size: 10+ task
Difficulté 5/5 Plus d'une semaine Accessibilité débutants 30/100
plotly/dash#4040 · 3 commentaires ·
Les mainteneurs répondent en général sous 2 jours
-
Drop Python 3.9 SupportOuverteP2 plotly-internal size: 5 task
Difficulté 4/5 3-5 jours Accessibilité débutants 35/100
plotly/dash#4038 · 2 commentaires ·
Les mainteneurs répondent en général sous 2 jours
Toutes les issues de plotly/dash
Issues similaires
-
Difficulté 1/5 Moins d'une heure Accessibilité débutants 83/100
PedestrianDynamics/pyFDS-Evac#766 ·
Les mainteneurs répondent en général sous 1 jour
-
Markdown tables render as literal text in 3 example files (missing blank line before header)Ouverte
Difficulté 1/5 1-3 heures Accessibilité débutants 91/100
alchaincyf/nuwa-skill#86 ·
-
Difficulté 2/5 1-3 heures Accessibilité débutants 76/100
Les mainteneurs répondent en général sous 2 jours
-
Docs Needs Triage
Difficulté 1/5 Moins d'une heure Accessibilité débutants 88/100
pandas-dev/pandas#71055 ·
Les mainteneurs répondent en général sous 1 jour
-
[Bug]: graphify reads files that git's global ignore file hidesPeut-être pris @smngvlkz l’a pris aujourd’hui. Ouverte
Difficulté 2/5 1-3 heures Accessibilité débutants 72/100
Graphify-Labs/graphify#4335 · 1 commentaire ·
Les mainteneurs répondent en général sous 1 jour