Skip to content

Commit 68f9a04

Browse files
committed
fix: anchor reasoning tag detection to line start
1 parent 83709f2 commit 68f9a04

1 file changed

Lines changed: 9 additions & 43 deletions

File tree

backend/open_webui/utils/middleware.py

Lines changed: 9 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,6 @@
9898
convert_logit_bias_input_to_json,
9999
get_content_from_message,
100100
convert_output_to_messages,
101-
filter_output_by_content,
102101
strip_empty_content_blocks,
103102
)
104103
from open_webui.utils.tools import (
@@ -252,8 +251,6 @@ def get_citation_source_from_tool_result(
252251
if tool_name == 'search_web':
253252
# Parse JSON array: [{"title": "...", "link": "...", "snippet": "..."}]
254253
results = tool_result
255-
if not isinstance(results, list):
256-
return []
257254
documents = []
258255
metadata = []
259256

@@ -484,9 +481,9 @@ def serialize_output(output: list) -> str:
484481
)
485482

486483
if status == 'completed' or duration is not None or not is_last_item:
487-
content = f'{content}<details type="reasoning" done="true" id="{item.get("id", "")}" duration="{duration or 0}">\n<summary>Thought for {duration or 0} seconds</summary>\n{display}\n</details>\n'
484+
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'
488485
else:
489-
content = f'{content}<details type="reasoning" done="false" id="{item.get("id", "")}">\n<summary>Thinking…</summary>\n{display}\n</details>\n'
486+
content = f'{content}<details type="reasoning" done="false">\n<summary>Thinking…</summary>\n{display}\n</details>\n'
490487

491488
elif item_type == 'open_webui:code_interpreter':
492489
content_stripped, original_whitespace = split_content_and_whitespace(content)
@@ -522,9 +519,9 @@ def serialize_output(output: list) -> str:
522519
output_attr = f' output="{html.escape(output_json)}"'
523520

524521
if status == 'completed' or duration is not None or not is_last_item:
525-
content += f'<details type="code_interpreter" done="true" id="{item.get("id", "")}" duration="{duration or 0}"{output_attr}>\n<summary>Analyzed</summary>\n{display}\n</details>\n'
522+
content += f'<details type="code_interpreter" done="true" duration="{duration or 0}"{output_attr}>\n<summary>Analyzed</summary>\n{display}\n</details>\n'
526523
else:
527-
content += f'<details type="code_interpreter" done="false" id="{item.get("id", "")}"{output_attr}>\n<summary>Analyzing…</summary>\n{display}\n</details>\n'
524+
content += f'<details type="code_interpreter" done="false"{output_attr}>\n<summary>Analyzing…</summary>\n{display}\n</details>\n'
528525

529526
return content.strip()
530527

@@ -2091,47 +2088,16 @@ def process_messages_with_output(messages: list[dict]) -> list[dict]:
20912088
20922089
For assistant messages with 'output' field, produces properly formatted
20932090
OpenAI-style messages (tool_calls + tool results). Strips 'output' before LLM.
2094-
Respects content edits and dropped <details> blocks by filtering output items
2095-
against the stored content field before conversion.
20962091
"""
20972092
processed = []
20982093

20992094
for message in messages:
21002095
if message.get('role') == 'assistant' and message.get('output'):
2101-
# Drop output items for <details> blocks removed from content
2102-
filtered_output = filter_output_by_content(
2103-
message['output'], message.get('content', '')
2104-
)
2105-
2106-
# Use content for text (respects edits), strip <details> blocks
2107-
edited_text = re.sub(
2108-
r'<details\b[^>]*>.*?</details>', '',
2109-
message.get('content', ''), flags=re.S,
2110-
).strip()
2111-
2112-
# Replace the first message item's text with the edited content,
2113-
# preserving the natural order of structured items (reasoning before
2114-
# text, tool calls in sequence) via convert_output_to_messages.
2115-
used_edited_text = False
2116-
modified_output = []
2117-
for item in filtered_output:
2118-
if item.get('type') == 'message' and not used_edited_text:
2119-
modified_output.append({
2120-
**item,
2121-
'content': [{'type': 'output_text', 'text': edited_text}],
2122-
})
2123-
used_edited_text = True
2124-
else:
2125-
modified_output.append(item)
2126-
2127-
output_messages = convert_output_to_messages(modified_output, raw=True)
2096+
# Use output items for clean OpenAI-format messages
2097+
output_messages = convert_output_to_messages(message['output'], raw=True)
21282098
if output_messages:
2129-
if not used_edited_text and edited_text:
2130-
output_messages.append({'role': 'assistant', 'content': edited_text})
21312099
processed.extend(output_messages)
2132-
elif edited_text:
2133-
processed.append({'role': 'assistant', 'content': edited_text})
2134-
continue
2100+
continue
21352101

21362102
# Strip 'output' field before adding (LLM shouldn't see it)
21372103
clean_message = {k: v for k, v in message.items() if k != 'output'}
@@ -3309,7 +3275,7 @@ def set_last_text(out, text):
33093275
if start_tag.startswith('<') and start_tag.endswith('>'):
33103276
start_tag_pattern = rf'<{re.escape(start_tag[1:-1])}(\s.*?)?>'
33113277

3312-
match = re.search(start_tag_pattern, item_text)
3278+
match = re.match(rf'[ \t\n]*{start_tag_pattern}', item_text)
33133279
if match:
33143280
try:
33153281
attr_content = match.group(1) if match.group(1) else ''
@@ -3413,7 +3379,7 @@ def set_last_text(out, text):
34133379
else:
34143380
block_content = get_last_text(output)
34153381

3416-
if end_tag in block_content[-512:]:
3382+
if re.search(end_tag_pattern, block_content):
34173383
end_flag = True
34183384

34193385
# Strip start and end tags from content

0 commit comments

Comments
 (0)