fix: stop every task of a chat when stopping a response without Redis (#29844)

Pressing Stop on a chat that has more than one running task (a multi-model
response, or a follow-up sent from another tab or device while a response
is still streaming) reported success but only cancelled the first task.
The survivors kept streaming and kept executing tool calls until the
iteration limit, which is the runaway reported in the issue.

Without Redis the stop loop iterates the live task-id list of the chat.
Each awaited cancellation runs that task's cleanup, which removes its id
from the same list mid-iteration, so the loop runs out one element early
and the last task is never cancelled. Returning a snapshot of the list to
callers keeps the loop on the ids it started with. Redis deployments
already got a fresh list from the set and were not affected.

Verified against a mock upstream that calls a tool on every turn: with three
tasks on one chat, stop left one or two alive before the change and cancels
all of them after it.

Fixes #29816
This commit is contained in:
Classic298 2026-09-12 20:55:13 +02:00 committed by GitHub
parent af54ea6283
commit 0313ea0238
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -166,7 +166,7 @@ async def list_task_ids_by_item_id(redis, id):
"""
if redis:
return await redis_list_item_tasks(redis, id)
return item_tasks.get(id, [])
return list(item_tasks.get(id, []))
async def save_response_stream(