diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index ebb51cb559..ab38e976ae 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -303,6 +303,13 @@ def tool_result_content(tool_result: Any) -> str: return str(tool_result) +def append_text(item: dict, key: str, value: str) -> None: + text = item[key] + item[key] = '' # dropping the dict's reference lets CPython extend the str in place + text += value + item[key] = text + + def merge_streamed_reasoning_details(target: list, details) -> None: items = details if isinstance(details, list) else [details] for item in items: @@ -321,7 +328,7 @@ def merge_streamed_reasoning_details(target: list, details) -> None: for key, value in item.items(): if key in ('text', 'summary') and isinstance(value, str) and isinstance(existing.get(key), str): - existing[key] += value + append_text(existing, key, value) else: existing[key] = value @@ -3470,7 +3477,7 @@ def update_assistant_message_from_stream(assistant_message, raw): def append_output_text(item, text): parts = item.setdefault('content', []) if parts and parts[-1].get('type') == 'output_text': - parts[-1]['text'] += text + append_text(parts[-1], 'text', text) else: parts.append({'type': 'output_text', 'text': text}) @@ -3545,7 +3552,8 @@ def update_assistant_message_from_stream(assistant_message, raw): append_output_text(output[-1], content) - assistant_message['content'] = assistant_message.get('content', '') + content + assistant_message.setdefault('content', '') + append_text(assistant_message, 'content', content) async def get_system_oauth_token(request, user): @@ -4663,7 +4671,7 @@ async def streaming_chat_response_handler(response, ctx): and isinstance(last_delta_data.get('delta'), str) and isinstance(delta_data.get('delta'), str) ): - last_delta_data['delta'] += delta_data['delta'] + append_text(last_delta_data, 'delta', delta_data['delta']) delta_count += 1 else: if last_delta_data and (last_delta_type != delta_type or last_delta_key != delta_key): @@ -4966,8 +4974,10 @@ async def streaming_chat_response_handler(response, ctx): str, ): current_response_tool_call['function']['arguments'] = '' - current_response_tool_call['function']['arguments'] += ( - delta_arguments + append_text( + current_response_tool_call['function'], + 'arguments', + delta_arguments, ) # Emit pending tool calls in real-time as Responses events. @@ -5136,7 +5146,7 @@ async def streaming_chat_response_handler(response, ctx): # Append to reasoning content parts = reasoning_item.get('content', []) if parts and parts[-1].get('type') == 'output_text': - parts[-1]['text'] += reasoning_content + append_text(parts[-1], 'text', reasoning_content) else: reasoning_item['content'] = [ { @@ -5238,7 +5248,7 @@ async def streaming_chat_response_handler(response, ctx): elif last_item_type == 'reasoning': parts = last_item.get('content', []) if parts and parts[-1].get('type') == 'output_text': - parts[-1]['text'] += value + append_text(parts[-1], 'text', value) else: last_item['content'] = [ { @@ -5250,7 +5260,7 @@ async def streaming_chat_response_handler(response, ctx): # solution or other _tag_type message msg_parts = last_item.get('content', []) if msg_parts and msg_parts[-1].get('type') == 'output_text': - msg_parts[-1]['text'] += value + append_text(msg_parts[-1], 'text', value) else: last_item['content'] = [ { @@ -5278,7 +5288,7 @@ async def streaming_chat_response_handler(response, ctx): # Append value to last message item's text msg_parts = output[-1].get('content', []) if msg_parts and msg_parts[-1].get('type') == 'output_text': - msg_parts[-1]['text'] += value + append_text(msg_parts[-1], 'text', value) else: output[-1]['content'] = [ {