From ece8312b7f01db906bfc712803404dc2e755a449 Mon Sep 17 00:00:00 2001
From: DrMelone <27028174+Classic298@users.noreply.github.com>
Date: Sun, 12 Apr 2026 22:19:59 +0200
Subject: [PATCH] perf: keep in-memory helpers sync to avoid coroutine overhead
Reverts content_endswith_newline, ensure_trailing_newline, and append_part back to sync def. Only serialize_output itself remains async as required by callsites. Avoids unnecessary coroutine creation in the hot streaming path.
---
backend/open_webui/utils/middleware.py | 30 +++++++++++++-------------
1 file changed, 15 insertions(+), 15 deletions(-)
diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py
index cce91da1e4..dfd5701327 100644
--- a/backend/open_webui/utils/middleware.py
+++ b/backend/open_webui/utils/middleware.py
@@ -389,14 +389,14 @@ def get_citation_source_from_tool_result(
]
-async def content_endswith_newline(parts):
+def content_endswith_newline(parts):
if not parts:
return False
return parts[-1].endswith('\n')
-async def ensure_trailing_newline(parts):
- if parts and not await content_endswith_newline(parts):
+def ensure_trailing_newline(parts):
+ if parts and not content_endswith_newline(parts):
parts.append('\n')
@@ -408,7 +408,7 @@ async def serialize_output(output: list) -> str:
content_parts = []
fence_count = 0
- async def append_part(part):
+ def append_part(part):
nonlocal fence_count
content_parts.append(part)
fence_count += part.count('```')
@@ -428,12 +428,12 @@ async def serialize_output(output: list) -> str:
if 'text' in content_part:
text = content_part.get('text', '').strip()
if text:
- await append_part(text)
- await append_part('\n')
+ append_part(text)
+ append_part('\n')
elif item_type == 'function_call':
# Render tool call inline with its result (if available)
- await ensure_trailing_newline(content_parts)
+ ensure_trailing_newline(content_parts)
call_id = item.get('call_id', '')
name = item.get('name', '')
@@ -450,9 +450,9 @@ async def serialize_output(output: list) -> str:
files = result_item.get('files')
embeds = result_item.get('embeds', '')
- await append_part(f'\nTool Executed
\n \n')
+ append_part(f'\nTool Executed
\n \n')
else:
- await append_part(f'\nExecuting...
\n \n')
+ append_part(f'\nExecuting...
\n \n')
elif item_type == 'function_call_output':
# Already handled inline with function_call above
@@ -477,7 +477,7 @@ async def serialize_output(output: list) -> str:
# render as done (a subsequent item means reasoning is complete)
is_last_item = idx == len(output) - 1
- await ensure_trailing_newline(content_parts)
+ ensure_trailing_newline(content_parts)
display = html.escape(
'\n'.join(
@@ -486,9 +486,9 @@ async def serialize_output(output: list) -> str:
)
if status == 'completed' or duration is not None or not is_last_item:
- await append_part(f'\nThought for {duration or 0} seconds
\n{display}\n \n')
+ append_part(f'\nThought for {duration or 0} seconds
\n{display}\n \n')
else:
- await append_part(f'\nThinking…
\n{display}\n \n')
+ append_part(f'\nThinking…
\n{display}\n \n')
elif item_type == 'open_webui:code_interpreter':
# Check if previous content ends with an opening code fence (e.g. ```python)
@@ -511,7 +511,7 @@ async def serialize_output(output: list) -> str:
while content_parts and not content_parts[-1].strip():
fence_count -= content_parts.pop().count('```')
- await ensure_trailing_newline(content_parts)
+ ensure_trailing_newline(content_parts)
# Render the code_interpreter item as a block
# so the frontend Collapsible renders "Analyzing..."/"Analyzed".
@@ -537,9 +537,9 @@ async def serialize_output(output: list) -> str:
output_attr = f' output="{html.escape(output_json)}"'
if status == 'completed' or duration is not None or not is_last_item:
- await append_part(f'\nAnalyzed
\n{display}\n \n')
+ append_part(f'\nAnalyzed
\n{display}\n \n')
else:
- await append_part(f'\nAnalyzing…
\n{display}\n \n')
+ append_part(f'\nAnalyzing…
\n{display}\n \n')
return ''.join(content_parts).strip()