From 58b36765a7c20f5943a3180bd289de48876d0878 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Wed, 16 Sep 2026 22:47:37 -0400 Subject: [PATCH] refac --- backend/open_webui/socket/main.py | 4 +++ backend/open_webui/utils/middleware.py | 29 +++++++++++++++---- .../chat/Messages/structuredOutput.ts | 5 ++++ 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/backend/open_webui/socket/main.py b/backend/open_webui/socket/main.py index 3f79bd77db..d4ac18f655 100644 --- a/backend/open_webui/socket/main.py +++ b/backend/open_webui/socket/main.py @@ -1,6 +1,7 @@ from __future__ import annotations import asyncio +import copy import logging import random import sys @@ -1010,6 +1011,9 @@ async def _make_channel_emitter(request_info): if not content and not output and not done: return + if isinstance(output, list): + state['output'] = copy.deepcopy(output) + now = time.time() if done or (now - state['last_emit_at']) >= THROTTLE_INTERVAL: state['last_emit_at'] = now diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index e0337fe553..5b59fbd7ad 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -4401,6 +4401,9 @@ async def streaming_chat_response_handler(response, ctx): Uses the text from the output items themselves for tag detection, eliminating state divergence between accumulated content and items. + + Mutates output in place; returns the rewritten output (or None + if no tag was consumed) and whether a block ended. """ end_flag = False @@ -4577,7 +4580,7 @@ async def streaming_chat_response_handler(response, ctx): if recursive_end: end_flag = True - break + return output, end_flag else: save_scanned_length(item, item_text) @@ -4666,10 +4669,11 @@ async def streaming_chat_response_handler(response, ctx): ], } ) + return output, end_flag else: save_scanned_length(item, block_content) - return output, end_flag + return None, False message = ( ctx.get('assistant_message') @@ -5563,25 +5567,31 @@ async def streaming_chat_response_handler(response, ctx): } ] + tag_output = None + if DETECT_REASONING_TAGS: - output, _ = tag_output_handler( + tag_output, _ = tag_output_handler( 'reasoning', reasoning_tags, output, ) - output, _ = tag_output_handler( + solution_output, _ = tag_output_handler( 'solution', DEFAULT_SOLUTION_TAGS, output, ) + if solution_output is not None: + tag_output = solution_output if DETECT_CODE_INTERPRETER: - output, end = tag_output_handler( + code_output, end = tag_output_handler( 'code_interpreter', DEFAULT_CODE_INTERPRETER_TAGS, output, ) + if code_output is not None: + tag_output = code_output if end: break @@ -5604,6 +5614,15 @@ async def streaming_chat_response_handler(response, ctx): } delta_type = delta_event_type + # the raw chunk still carries the tag text: resend the cleaned output instead + if tag_output is not None: + await flush_pending_delta_data() + await event_emitter( + {'type': 'chat:completion', 'data': {'output': full_output()}} + ) + await save_current_response_stream() + data = None + if delta and data: await queue_pending_delta_data(data, delta_type) elif data: diff --git a/src/lib/components/chat/Messages/structuredOutput.ts b/src/lib/components/chat/Messages/structuredOutput.ts index 55ef655a2b..bca4a0ff08 100644 --- a/src/lib/components/chat/Messages/structuredOutput.ts +++ b/src/lib/components/chat/Messages/structuredOutput.ts @@ -626,6 +626,11 @@ export function applyResponseStreamEvent( return nextOutput; } + if (item.type === 'open_webui:code_interpreter') { + item.code = `${item.code ?? ''}${event.delta ?? ''}`; + return nextOutput; + } + const key = deltaType === 'output_text' || deltaType === 'reasoning_text' ? 'text' : deltaType; item.content = [...(item.content ?? [])]; const part = ensurePart(item.content, event.content_index ?? 0);