fix message editing

This commit is contained in:
eniraa 2026-03-17 12:00:59 -07:00
parent 30068afd78
commit 89b04502ac
No known key found for this signature in database
4 changed files with 69 additions and 7 deletions

View file

@ -1784,6 +1784,7 @@ async def chat_completion(
"user_id": user.id,
"chat_id": form_data.pop("chat_id", None),
"message_id": form_data.pop("id", None),
"context_message_id": form_data.pop("context_message_id", None),
"parent_message": form_data.pop("parent_message", None),
"parent_message_id": form_data.pop("parent_id", None),
"session_id": form_data.pop("session_id", None),

View file

@ -97,6 +97,7 @@ from open_webui.utils.misc import (
convert_logit_bias_input_to_json,
get_content_from_message,
convert_output_to_messages,
merge_assistant_content_into_output_messages,
)
from open_webui.utils.tools import (
get_tools,
@ -2131,7 +2132,11 @@ def process_messages_with_output(messages: list[dict]) -> list[dict]:
# Use output items for clean OpenAI-format messages
output_messages = convert_output_to_messages(message["output"], raw=True)
if output_messages:
processed.extend(output_messages)
processed.extend(
merge_assistant_content_into_output_messages(
output_messages, message.get("content")
)
)
continue
# Strip 'output' field before adding (LLM shouldn't see it)
@ -2152,10 +2157,12 @@ async def process_chat_payload(request, form_data, user, metadata, model):
# Load messages from DB when available — DB preserves structured 'output' items
# which the frontend strips, causing tool calls to be merged into content.
chat_id = metadata.get("chat_id")
parent_message_id = metadata.get("parent_message_id")
context_message_id = metadata.get("context_message_id") or metadata.get(
"parent_message_id"
)
if chat_id and parent_message_id and not chat_id.startswith("local:"):
db_messages = load_messages_from_db(chat_id, parent_message_id)
if chat_id and context_message_id and not chat_id.startswith("local:"):
db_messages = load_messages_from_db(chat_id, context_message_id)
if db_messages:
system_message = get_system_message(form_data.get("messages", []))
form_data["messages"] = (

View file

@ -6,7 +6,7 @@ import uuid
import logging
from datetime import timedelta
from pathlib import Path
from typing import Callable, Optional, Sequence, Union
from typing import Any, Callable, Optional, Sequence, Union
import json
import aiohttp
import mimeparse
@ -275,6 +275,51 @@ def convert_output_to_messages(output: list, raw: bool = False) -> list[dict]:
return messages
def merge_assistant_content_into_output_messages(
messages: list[dict], content: Any
) -> list[dict]:
"""
Preserve tool/reasoning reconstruction from stored `output`, but let an
edited assistant `content` replace the final assistant-visible text.
This keeps the backend-authoritative message chain editable without
discarding tool calls or other structured output items.
"""
if not messages:
return messages
content_text = get_content_from_message({"content": content})
if not content_text or not content_text.strip():
return messages
merged_messages = list(messages)
assistant_indexes = [
idx
for idx, message in enumerate(merged_messages)
if message.get("role") == "assistant"
]
if not assistant_indexes:
return [*merged_messages, {"role": "assistant", "content": content}]
non_tool_assistant_indexes = [
idx
for idx in assistant_indexes
if not merged_messages[idx].get("tool_calls")
]
target_idx = (
non_tool_assistant_indexes[-1]
if non_tool_assistant_indexes
else assistant_indexes[-1]
)
merged_messages[target_idx] = {
**merged_messages[target_idx],
"content": content,
}
return merged_messages
def get_last_user_message(messages: list[dict]) -> Optional[str]:
message = get_last_user_message_item(messages)
if message is None:

View file

@ -2030,7 +2030,14 @@
.map((token) => decodeURIComponent(JSON.parse(`"${token.replace(/"/g, '\\"')}"`)));
};
const sendMessageSocket = async (model, _messages, _history, responseMessageId, _chatId) => {
const sendMessageSocket = async (
model,
_messages,
_history,
responseMessageId,
_chatId,
{ contextMessageId = null }: { contextMessageId?: string | null } = {}
) => {
const responseMessage = _history.messages[responseMessageId];
const userMessage = _history.messages[responseMessage.parentId];
@ -2223,6 +2230,7 @@
chat_id: $chatId,
id: responseMessageId,
context_message_id: contextMessageId ?? undefined,
parent_id: userMessage?.id ?? null,
parent_message: userMessage,
@ -2458,7 +2466,8 @@
createMessagesList(history, responseMessage.id),
history,
responseMessage.id,
_chatId
_chatId,
{ contextMessageId: responseMessage.id }
);
}
}