diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py
index 9ab5c18b74..cce91da1e4 100644
--- a/backend/open_webui/utils/middleware.py
+++ b/backend/open_webui/utils/middleware.py
@@ -389,18 +389,18 @@ def get_citation_source_from_tool_result(
]
-def content_endswith_newline(parts):
+async def content_endswith_newline(parts):
if not parts:
return False
return parts[-1].endswith('\n')
-def ensure_trailing_newline(parts):
- if parts and not content_endswith_newline(parts):
+async def ensure_trailing_newline(parts):
+ if parts and not await content_endswith_newline(parts):
parts.append('\n')
-def serialize_output(output: list) -> str:
+async def serialize_output(output: list) -> str:
"""
Convert OR-aligned output items to HTML for display.
For LLM consumption, use convert_output_to_messages() instead.
@@ -408,7 +408,7 @@ def serialize_output(output: list) -> str:
content_parts = []
fence_count = 0
- def append_part(part):
+ async def append_part(part):
nonlocal fence_count
content_parts.append(part)
fence_count += part.count('```')
@@ -428,12 +428,12 @@ def serialize_output(output: list) -> str:
if 'text' in content_part:
text = content_part.get('text', '').strip()
if text:
- append_part(text)
- append_part('\n')
+ await append_part(text)
+ await append_part('\n')
elif item_type == 'function_call':
# Render tool call inline with its result (if available)
- ensure_trailing_newline(content_parts)
+ await ensure_trailing_newline(content_parts)
call_id = item.get('call_id', '')
name = item.get('name', '')
@@ -450,9 +450,9 @@ def serialize_output(output: list) -> str:
files = result_item.get('files')
embeds = result_item.get('embeds', '')
- append_part(f'\nTool Executed
\n \n')
+ await append_part(f'\nTool Executed
\n \n')
else:
- append_part(f'\nExecuting...
\n \n')
+ await append_part(f'\nExecuting...
\n \n')
elif item_type == 'function_call_output':
# Already handled inline with function_call above
@@ -477,7 +477,7 @@ def serialize_output(output: list) -> str:
# render as done (a subsequent item means reasoning is complete)
is_last_item = idx == len(output) - 1
- ensure_trailing_newline(content_parts)
+ await ensure_trailing_newline(content_parts)
display = html.escape(
'\n'.join(
@@ -486,9 +486,9 @@ def serialize_output(output: list) -> str:
)
if status == 'completed' or duration is not None or not is_last_item:
- append_part(f'\nThought for {duration or 0} seconds
\n{display}\n \n')
+ await append_part(f'\nThought for {duration or 0} seconds
\n{display}\n \n')
else:
- append_part(f'\nThinking…
\n{display}\n \n')
+ await 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 @@ def serialize_output(output: list) -> str:
while content_parts and not content_parts[-1].strip():
fence_count -= content_parts.pop().count('```')
- ensure_trailing_newline(content_parts)
+ await ensure_trailing_newline(content_parts)
# Render the code_interpreter item as a block
# so the frontend Collapsible renders "Analyzing..."/"Analyzed".
@@ -537,9 +537,9 @@ 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:
- append_part(f'\nAnalyzed
\n{display}\n \n')
+ await append_part(f'\nAnalyzed
\n{display}\n \n')
else:
- append_part(f'\nAnalyzing…
\n{display}\n \n')
+ await append_part(f'\nAnalyzing…
\n{display}\n \n')
return ''.join(content_parts).strip()
@@ -3617,7 +3617,7 @@ async def streaming_chat_response_handler(response, ctx):
processed_data = {
'output': full_output(),
- 'content': serialize_output(full_output()),
+ 'content': await serialize_output(full_output()),
}
# print(data)
@@ -3776,7 +3776,7 @@ async def streaming_chat_response_handler(response, ctx):
{
'type': 'chat:completion',
'data': {
- 'content': serialize_output(full_output() + pending_fc_items),
+ 'content': await serialize_output(full_output() + pending_fc_items),
},
}
)
@@ -3835,7 +3835,7 @@ async def streaming_chat_response_handler(response, ctx):
}
]
- data = {'content': serialize_output(full_output())}
+ data = {'content': await serialize_output(full_output())}
if value:
if (
@@ -3986,13 +3986,13 @@ async def streaming_chat_response_handler(response, ctx):
metadata['chat_id'],
metadata['message_id'],
{
- 'content': serialize_output(full_output()),
+ 'content': await serialize_output(full_output()),
'output': full_output(),
},
)
else:
data = {
- 'content': serialize_output(full_output()),
+ 'content': await serialize_output(full_output()),
}
if delta:
@@ -4130,7 +4130,7 @@ async def streaming_chat_response_handler(response, ctx):
{
'type': 'chat:completion',
'data': {
- 'content': serialize_output(full_output()),
+ 'content': await serialize_output(full_output()),
'output': full_output(),
},
}
@@ -4382,7 +4382,7 @@ async def streaming_chat_response_handler(response, ctx):
{
'type': 'chat:completion',
'data': {
- 'content': serialize_output(output),
+ 'content': await serialize_output(output),
'output': frontend_output,
},
}
@@ -4481,7 +4481,7 @@ async def streaming_chat_response_handler(response, ctx):
{
'type': 'chat:completion',
'data': {
- 'content': serialize_output(output),
+ 'content': await serialize_output(output),
'output': output,
},
}
@@ -4605,7 +4605,7 @@ async def streaming_chat_response_handler(response, ctx):
{
'type': 'chat:completion',
'data': {
- 'content': serialize_output(output),
+ 'content': await serialize_output(output),
'output': output,
},
}
@@ -4645,7 +4645,7 @@ async def streaming_chat_response_handler(response, ctx):
title = await Chats.get_chat_title_by_id(metadata['chat_id'])
data = {
'done': True,
- 'content': serialize_output(output),
+ 'content': await serialize_output(output),
'output': output,
'title': title,
**({'usage': usage} if usage else {}),
@@ -4658,7 +4658,7 @@ async def streaming_chat_response_handler(response, ctx):
metadata['message_id'],
{
'done': True,
- 'content': serialize_output(output),
+ 'content': await serialize_output(output),
'output': output,
**({'usage': usage} if usage else {}),
},
@@ -4711,7 +4711,7 @@ async def streaming_chat_response_handler(response, ctx):
metadata['message_id'],
{
'done': True,
- 'content': serialize_output(output),
+ 'content': await serialize_output(output),
'output': output,
},
)