From 728f397ff4b8b36153029f3600ee1b5e374c1ea8 Mon Sep 17 00:00:00 2001 From: Classic298 <27028174+Classic298@users.noreply.github.com> Date: Wed, 7 Oct 2026 13:04:52 +0200 Subject: [PATCH] fix: earlier tool calls are sent twice after answering the model's question or pressing Continue (#32029) When a reply was paused and then picked up again, either by answering a question from the built-in Ask User tool or by pressing Continue, every tool round from the second one on sent everything from before the pause to the model twice. Providers that reject repeated tool calls, like DeepSeek, then fail with "Duplicate 'call_id'" and the chat stops, while others quietly see the earlier tool calls and text twice. Everything from before the pause is now sent exactly once. Tested against a mock provider that records every request: three tool rounds after an answered question and after Continue send everything once, and replies that were never paused send exactly what they sent before. Fixes #31991 --- backend/open_webui/utils/middleware.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index 188c25fb03..1e211c9ae7 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -6183,6 +6183,8 @@ async def streaming_chat_response_handler(response, ctx): # keeps indices aligned. The display prefix # ensures the UI shows tool history during # streaming. + continued_output = prior_output + round_output = output prior_output = list(full_output()) # Trim the trailing empty placeholder message # so it doesn't persist as a ghost item once @@ -6195,11 +6197,13 @@ async def streaming_chat_response_handler(response, ctx): msg_parts = prior_output[-1].get('content', []) if not msg_parts or (len(msg_parts) == 1 and not msg_parts[0].get('text', '').strip()): prior_output.pop() + round_output = round_output[:-1] output = [] output_start = len(prior_output) await stream_body_handler(res, new_form_data) - output = full_output() - prior_output = [] + # A continued reply's earlier items are already in form_data['messages'] + output = [*round_output, *output] + prior_output = continued_output elif getattr(res, 'status_code', 200) >= 400: await emit_message_error(get_message_error_content(get_response_error_detail(res))) break @@ -6411,7 +6415,7 @@ async def streaming_chat_response_handler(response, ctx): break # Mark all in-progress items as completed - for item in output: + for item in [*prior_output, *output]: if item.get('status') == 'in_progress': item['status'] = 'completed'