Skip to content

Commit 49430de

Browse files
committed
refac
1 parent 1be9627 commit 49430de

1 file changed

Lines changed: 21 additions & 26 deletions

File tree

backend/open_webui/utils/middleware.py

Lines changed: 21 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,7 @@ def serialize_output(output: list) -> str:
455455
Convert OR-aligned output items to HTML for display.
456456
For LLM consumption, use convert_output_to_messages() instead.
457457
"""
458-
content = ''
458+
parts: list[str] = []
459459

460460
# First pass: collect function_call_output items by call_id for lookup
461461
tool_outputs = {}
@@ -472,53 +472,48 @@ def serialize_output(output: list) -> str:
472472
if 'text' in content_part:
473473
text = content_part.get('text', '').strip()
474474
if text:
475-
content = f'{content}{text}\n'
475+
parts.append(text)
476476

477477
elif item_type == 'function_call':
478-
# Render tool call inline with its result (if available)
479-
if content and not content.endswith('\n'):
480-
content += '\n'
481-
482478
call_id = item.get('call_id', '')
483479
name = item.get('name', '')
484480
arguments = item.get('arguments', '')
485481

486482
result_item = tool_outputs.get(call_id)
487483
if result_item:
488-
result_text = ''
484+
result_parts: list[str] = []
489485
for result_output in result_item.get('output', []):
490486
if 'text' in result_output:
491487
output_text = result_output.get('text', '')
492-
result_text += str(output_text) if not isinstance(output_text, str) else output_text
488+
result_parts.append(str(output_text) if not isinstance(output_text, str) else output_text)
489+
result_text = ''.join(result_parts)
493490
files = result_item.get('files')
494491
embeds = result_item.get('embeds', '')
495492

496-
content += f'<details type="tool_calls" done="true" id="{call_id}" name="{name}" arguments="{html.escape(json.dumps(arguments))}" files="{html.escape(json.dumps(files)) if files else ""}" embeds="{html.escape(json.dumps(embeds))}">\n<summary>Tool Executed</summary>\n{html.escape(json.dumps(result_text, ensure_ascii=False))}\n</details>\n'
493+
parts.append(f'<details type="tool_calls" done="true" id="{call_id}" name="{name}" arguments="{html.escape(json.dumps(arguments))}" files="{html.escape(json.dumps(files)) if files else ""}" embeds="{html.escape(json.dumps(embeds))}">\n<summary>Tool Executed</summary>\n{html.escape(json.dumps(result_text, ensure_ascii=False))}\n</details>')
497494
else:
498-
content += f'<details type="tool_calls" done="false" id="{call_id}" name="{name}" arguments="{html.escape(json.dumps(arguments))}">\n<summary>Executing...</summary>\n</details>\n'
495+
parts.append(f'<details type="tool_calls" done="false" id="{call_id}" name="{name}" arguments="{html.escape(json.dumps(arguments))}">\n<summary>Executing...</summary>\n</details>')
499496

500497
elif item_type == 'function_call_output':
501498
# Already handled inline with function_call above
502499
pass
503500

504501
elif item_type in _OPENAI_TOOL_DISPLAY_NAMES:
505-
if content and not content.endswith('\n'):
506-
content += '\n'
507502
status = item.get('status', 'in_progress')
508503
done = status in ('completed', 'failed', 'incomplete') or idx != len(output) - 1
509-
content += _render_openai_tool_call_handler(item, done)
504+
parts.append(_render_openai_tool_call_handler(item, done).rstrip('\n'))
510505

511506
elif item_type == 'reasoning':
512-
reasoning_content = ''
507+
reasoning_parts: list[str] = []
513508
# Check for 'summary' (new structure) or 'content' (legacy/fallback)
514509
source_list = item.get('summary', []) or item.get('content', [])
515510
for content_part in source_list:
516511
if 'text' in content_part:
517-
reasoning_content += content_part.get('text', '')
512+
reasoning_parts.append(content_part.get('text', ''))
518513
elif 'summary' in content_part: # Handle potential nested logic if any
519514
pass
520515

521-
reasoning_content = reasoning_content.strip()
516+
reasoning_content = ''.join(reasoning_parts).strip()
522517

523518
duration = item.get('duration')
524519
status = item.get('status', 'in_progress')
@@ -527,29 +522,29 @@ def serialize_output(output: list) -> str:
527522
# render as done (a subsequent item means reasoning is complete)
528523
is_last_item = idx == len(output) - 1
529524

530-
if content and not content.endswith('\n'):
531-
content += '\n'
532-
533525
display = html.escape(
534526
'\n'.join(
535527
(f'> {line}' if not line.startswith('>') else line) for line in reasoning_content.splitlines()
536528
)
537529
)
538530

539531
if status == 'completed' or duration is not None or not is_last_item:
540-
content = f'{content}<details type="reasoning" done="true" duration="{duration or 0}">\n<summary>Thought for {duration or 0} seconds</summary>\n{display}\n</details>\n'
532+
parts.append(f'<details type="reasoning" done="true" duration="{duration or 0}">\n<summary>Thought for {duration or 0} seconds</summary>\n{display}\n</details>')
541533
else:
542-
content = f'{content}<details type="reasoning" done="false">\n<summary>Thinking…</summary>\n{display}\n</details>\n'
534+
parts.append(f'<details type="reasoning" done="false">\n<summary>Thinking…</summary>\n{display}\n</details>')
543535

544536
elif item_type == 'open_webui:code_interpreter':
537+
# Code interpreter needs to inspect/mutate prior accumulated content
538+
# to strip trailing unclosed code fences — materialize only here.
539+
content = '\n'.join(parts)
545540
content_stripped, original_whitespace = split_content_and_whitespace(content)
546541
if is_opening_code_block(content_stripped):
547542
content = content_stripped.rstrip('`').rstrip() + original_whitespace
548543
else:
549544
content = content_stripped + original_whitespace
550545

551-
if content and not content.endswith('\n'):
552-
content += '\n'
546+
# Re-split back into parts list after mutation
547+
parts = [content] if content else []
553548

554549
# Render the code_interpreter item as a <details> block
555550
# so the frontend Collapsible renders "Analyzing..."/"Analyzed".
@@ -575,11 +570,11 @@ def serialize_output(output: list) -> str:
575570
output_attr = f' output="{html.escape(output_json)}"'
576571

577572
if status == 'completed' or duration is not None or not is_last_item:
578-
content += f'<details type="code_interpreter" done="true" duration="{duration or 0}"{output_attr}>\n<summary>Analyzed</summary>\n{display}\n</details>\n'
573+
parts.append(f'<details type="code_interpreter" done="true" duration="{duration or 0}"{output_attr}>\n<summary>Analyzed</summary>\n{display}\n</details>')
579574
else:
580-
content += f'<details type="code_interpreter" done="false"{output_attr}>\n<summary>Analyzing…</summary>\n{display}\n</details>\n'
575+
parts.append(f'<details type="code_interpreter" done="false"{output_attr}>\n<summary>Analyzing…</summary>\n{display}\n</details>')
581576

582-
return content.strip()
577+
return '\n'.join(parts).strip()
583578

584579

585580
def deep_merge(target, source):

0 commit comments

Comments
 (0)