diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index be5ad7ae1c..8c77b09610 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -6374,14 +6374,30 @@ async def streaming_chat_response_handler(response, ctx): pass async def save_cancelled_state(): - await event_emitter({'type': 'chat:tasks:cancel'}) + cancelled_output = full_output() + result_call_ids = { + item.get('call_id') + for item in cancelled_output + if item.get('type') == 'function_call_output' and item.get('call_id') + } + for item in cancelled_output: + # A tool call is stamped completed when its arguments finish, before the tool runs + is_running_tool_call = ( + item.get('type') == 'function_call' + and item.get('status') == 'completed' + and (item.get('call_id') or item.get('id')) not in result_call_ids + ) + if item.get('status') == 'in_progress' or is_running_tool_call: + item['status'] = 'incomplete' + + await event_emitter({'type': 'chat:tasks:cancel', 'data': {'output': cancelled_output}}) if save_to_chat: await Chats.upsert_message_to_chat_by_id_and_message_id( metadata['chat_id'], metadata['message_id'], { 'done': True, - 'output': full_output(), + 'output': cancelled_output, }, ) await clear_response_stream(request.app.state.redis, response_stream_task_id) diff --git a/src/lib/components/chat/Chat.svelte b/src/lib/components/chat/Chat.svelte index edaa51c8fa..14cf24f4c1 100644 --- a/src/lib/components/chat/Chat.svelte +++ b/src/lib/components/chat/Chat.svelte @@ -1237,6 +1237,9 @@ chatCompletionEventHandler(data, message, event.chat_id); } else if (type === 'chat:tasks:cancel') { dismissContextCompactionToast(); + if (data?.output) { + message.output = data.output; + } if (event.message_id === history.currentId) { taskIds = null; // Set all response messages to done