mirror of
https://github.com/open-webui/open-webui.git
synced 2026-10-07 02:58:21 +00:00
fix message editing
This commit is contained in:
parent
30068afd78
commit
89b04502ac
4 changed files with 69 additions and 7 deletions
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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"] = (
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue