From 0b75445ff9a42e37640c034812d0de9b84039e60 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Tue, 30 Jun 2026 20:20:10 -0500 Subject: [PATCH] refac --- backend/open_webui/utils/middleware.py | 62 +++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 7 deletions(-) diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index c9121120f7..8bd0b175b0 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -2905,6 +2905,13 @@ def update_assistant_message_from_stream(assistant_message, raw): if not isinstance(line, str): return + def append_output_text(item, text): + parts = item.setdefault('content', []) + if parts and parts[-1].get('type') == 'output_text': + parts[-1]['text'] += text + else: + parts.append({'type': 'output_text', 'text': text}) + for raw_part in line.splitlines(): part = raw_part.removeprefix('data:').strip() if not part or part == '[DONE]': @@ -2932,8 +2939,50 @@ def update_assistant_message_from_stream(assistant_message, raw): assistant_message['usage'] = merge_usage(assistant_message.get('usage'), raw_usage) for choice in data.get('choices', []): - content = (choice.get('delta', {}) or {}).get('content') + delta = choice.get('delta', {}) or {} + content = delta.get('content') + reasoning_content = delta.get('reasoning_content') or delta.get('reasoning') or delta.get('thinking') + + if reasoning_content: + output = assistant_message.setdefault('output', []) + if not output or output[-1].get('type') != 'reasoning': + output.append( + { + 'type': 'reasoning', + 'id': output_id('r'), + 'status': 'in_progress', + 'start_tag': '', + 'end_tag': '', + 'attributes': {'type': 'reasoning_content'}, + 'content': [], + 'summary': None, + 'started_at': time.time(), + } + ) + + append_output_text(output[-1], reasoning_content) + if content: + output = assistant_message.get('output') + if output: + if output[-1].get('type') == 'reasoning': + output[-1]['status'] = 'completed' + output[-1]['ended_at'] = time.time() + output[-1]['duration'] = int(output[-1]['ended_at'] - output[-1]['started_at']) + + if not output or output[-1].get('type') != 'message': + output.append( + { + 'type': 'message', + 'id': output_id('msg'), + 'status': 'in_progress', + 'role': 'assistant', + 'content': [], + } + ) + + append_output_text(output[-1], content) + assistant_message['content'] = assistant_message.get('content', '') + content @@ -3395,10 +3444,12 @@ async def non_streaming_chat_response_handler(response, ctx): ) choices = response_data.get('choices', []) - if choices and choices[0].get('message', {}).get('content'): - content = response_data['choices'][0]['message']['content'] + response_output = response_data.get('output') + content = choices[0].get('message', {}).get('content') if choices else '' - if content: + if choices and (content or response_output): + + if content or response_output: await event_emitter( { 'type': 'chat:completion', @@ -3414,7 +3465,6 @@ async def non_streaming_chat_response_handler(response, ctx): # Use output from backend if provided (OR-compliant backends), # otherwise generate from response content - response_output = response_data.get('output') if not response_output: choice_message = choices[0].get('message', {}) reasoning_content = choice_message.get('reasoning_content') or choice_message.get('reasoning') @@ -3453,7 +3503,6 @@ async def non_streaming_chat_response_handler(response, ctx): 'type': 'chat:completion', 'data': { 'done': True, - 'content': content, 'output': response_output, 'title': title, }, @@ -3470,7 +3519,6 @@ async def non_streaming_chat_response_handler(response, ctx): { 'done': True, 'role': 'assistant', - 'content': content, 'output': response_output, **({'usage': usage} if usage else {}), },