|
94 | 94 | convert_logit_bias_input_to_json, |
95 | 95 | get_content_from_message, |
96 | 96 | convert_output_to_messages, |
| 97 | + filter_output_by_content, |
97 | 98 | ) |
98 | 99 | from open_webui.utils.tools import ( |
99 | 100 | get_tools, |
@@ -417,9 +418,9 @@ def serialize_output(output: list) -> str: |
417 | 418 | ) |
418 | 419 |
|
419 | 420 | if status == "completed" or duration is not None or not is_last_item: |
420 | | - 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' |
| 421 | + 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' |
421 | 422 | else: |
422 | | - content = f'{content}<details type="reasoning" done="false">\n<summary>Thinking…</summary>\n{display}\n</details>\n' |
| 423 | + content = f'{content}<details type="reasoning" done="false" id="{item.get("id", "")}">\n<summary>Thinking…</summary>\n{display}\n</details>\n' |
423 | 424 |
|
424 | 425 | elif item_type == "open_webui:code_interpreter": |
425 | 426 | content_stripped, original_whitespace = split_content_and_whitespace( |
@@ -459,9 +460,9 @@ def serialize_output(output: list) -> str: |
459 | 460 | output_attr = f' output="{html.escape(output_json)}"' |
460 | 461 |
|
461 | 462 | if status == "completed" or duration is not None or not is_last_item: |
462 | | - content += f'<details type="code_interpreter" done="true" duration="{duration or 0}"{output_attr}>\n<summary>Analyzed</summary>\n{display}\n</details>\n' |
| 463 | + 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' |
463 | 464 | else: |
464 | | - content += f'<details type="code_interpreter" done="false"{output_attr}>\n<summary>Analyzing…</summary>\n{display}\n</details>\n' |
| 465 | + content += f'<details type="code_interpreter" done="false" id="{item.get("id", "")}"{output_attr}>\n<summary>Analyzing…</summary>\n{display}\n</details>\n' |
465 | 466 |
|
466 | 467 | return content.strip() |
467 | 468 |
|
@@ -1970,11 +1971,40 @@ def process_messages_with_output(messages: list[dict]) -> list[dict]: |
1970 | 1971 |
|
1971 | 1972 | for message in messages: |
1972 | 1973 | if message.get("role") == "assistant" and message.get("output"): |
1973 | | - # Use output items for clean OpenAI-format messages |
1974 | | - output_messages = convert_output_to_messages(message["output"]) |
| 1974 | + # Drop output items for <details> blocks removed from content |
| 1975 | + output = filter_output_by_content( |
| 1976 | + message["output"], message.get("content", "") |
| 1977 | + ) |
| 1978 | + |
| 1979 | + # Use content for text (respects edits), output for structured items |
| 1980 | + content = re.sub( |
| 1981 | + r"<details\b[^>]*>.*?</details>", "", |
| 1982 | + message.get("content", ""), flags=re.S, |
| 1983 | + ).strip() |
| 1984 | + non_message_items = [ |
| 1985 | + i for i in output if i.get("type") != "message" |
| 1986 | + ] |
| 1987 | + output_messages = convert_output_to_messages(non_message_items) |
| 1988 | + |
1975 | 1989 | if output_messages: |
| 1990 | + # Prepend edited text to first assistant message |
| 1991 | + for om in output_messages: |
| 1992 | + if om.get("role") == "assistant": |
| 1993 | + om["content"] = ( |
| 1994 | + (content + "\n" + om["content"]).strip() |
| 1995 | + if om.get("content") |
| 1996 | + else content |
| 1997 | + ) |
| 1998 | + content = "" |
| 1999 | + break |
| 2000 | + if content: |
| 2001 | + output_messages.insert( |
| 2002 | + 0, {"role": "assistant", "content": content} |
| 2003 | + ) |
1976 | 2004 | processed.extend(output_messages) |
1977 | | - continue |
| 2005 | + elif content: |
| 2006 | + processed.append({"role": "assistant", "content": content}) |
| 2007 | + continue |
1978 | 2008 |
|
1979 | 2009 | # Strip 'output' field before adding (LLM shouldn't see it) |
1980 | 2010 | clean_message = {k: v for k, v in message.items() if k != "output"} |
|
0 commit comments