|
97 | 97 | convert_logit_bias_input_to_json, |
98 | 98 | get_content_from_message, |
99 | 99 | convert_output_to_messages, |
| 100 | + filter_output_by_content, |
100 | 101 | ) |
101 | 102 | from open_webui.utils.tools import ( |
102 | 103 | get_tools, |
@@ -485,9 +486,9 @@ def serialize_output(output: list) -> str: |
485 | 486 | ) |
486 | 487 |
|
487 | 488 | if status == "completed" or duration is not None or not is_last_item: |
488 | | - 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' |
| 489 | + 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' |
489 | 490 | else: |
490 | | - content = f'{content}<details type="reasoning" done="false">\n<summary>Thinking…</summary>\n{display}\n</details>\n' |
| 491 | + content = f'{content}<details type="reasoning" done="false" id="{item.get("id", "")}">\n<summary>Thinking…</summary>\n{display}\n</details>\n' |
491 | 492 |
|
492 | 493 | elif item_type == "open_webui:code_interpreter": |
493 | 494 | content_stripped, original_whitespace = split_content_and_whitespace( |
@@ -527,9 +528,9 @@ def serialize_output(output: list) -> str: |
527 | 528 | output_attr = f' output="{html.escape(output_json)}"' |
528 | 529 |
|
529 | 530 | if status == "completed" or duration is not None or not is_last_item: |
530 | | - content += f'<details type="code_interpreter" done="true" duration="{duration or 0}"{output_attr}>\n<summary>Analyzed</summary>\n{display}\n</details>\n' |
| 531 | + 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' |
531 | 532 | else: |
532 | | - content += f'<details type="code_interpreter" done="false"{output_attr}>\n<summary>Analyzing…</summary>\n{display}\n</details>\n' |
| 533 | + content += f'<details type="code_interpreter" done="false" id="{item.get("id", "")}"{output_attr}>\n<summary>Analyzing…</summary>\n{display}\n</details>\n' |
533 | 534 |
|
534 | 535 | return content.strip() |
535 | 536 |
|
@@ -2128,11 +2129,40 @@ def process_messages_with_output(messages: list[dict]) -> list[dict]: |
2128 | 2129 |
|
2129 | 2130 | for message in messages: |
2130 | 2131 | if message.get("role") == "assistant" and message.get("output"): |
2131 | | - # Use output items for clean OpenAI-format messages |
2132 | | - output_messages = convert_output_to_messages(message["output"], raw=True) |
| 2132 | + # Drop output items for <details> blocks removed from content |
| 2133 | + output = filter_output_by_content( |
| 2134 | + message["output"], message.get("content", "") |
| 2135 | + ) |
| 2136 | + |
| 2137 | + # Use content for text (respects edits), output for structured items |
| 2138 | + content = re.sub( |
| 2139 | + r"<details\b[^>]*>.*?</details>", "", |
| 2140 | + message.get("content", ""), flags=re.S, |
| 2141 | + ).strip() |
| 2142 | + non_message_items = [ |
| 2143 | + i for i in output if i.get("type") != "message" |
| 2144 | + ] |
| 2145 | + output_messages = convert_output_to_messages(non_message_items, raw=True) |
| 2146 | + |
2133 | 2147 | if output_messages: |
| 2148 | + # Prepend edited text to first assistant message |
| 2149 | + for om in output_messages: |
| 2150 | + if om.get("role") == "assistant": |
| 2151 | + om["content"] = ( |
| 2152 | + (content + "\n" + om["content"]).strip() |
| 2153 | + if om.get("content") |
| 2154 | + else content |
| 2155 | + ) |
| 2156 | + content = "" |
| 2157 | + break |
| 2158 | + if content: |
| 2159 | + output_messages.insert( |
| 2160 | + 0, {"role": "assistant", "content": content} |
| 2161 | + ) |
2134 | 2162 | processed.extend(output_messages) |
2135 | | - continue |
| 2163 | + elif content: |
| 2164 | + processed.append({"role": "assistant", "content": content}) |
| 2165 | + continue |
2136 | 2166 |
|
2137 | 2167 | # Strip 'output' field before adding (LLM shouldn't see it) |
2138 | 2168 | clean_message = {k: v for k, v in message.items() if k != "output"} |
|
0 commit comments