diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index 64adaef668..6a3e4b6f5b 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -2504,6 +2504,10 @@ async def process_chat_payload(request, form_data, user, metadata, model): except Exception: log.exception('Context compaction failed; continuing with full chat history') + metadata['context_start_message_id'] = next( + (message.get('id') for message in form_data.get('messages', []) if message.get('role') != 'system'), None + ) + # Process messages with OR-aligned output items for clean LLM messages for message in form_data.get('messages', []): output = message.get('output') @@ -3523,6 +3527,11 @@ async def drain_approved_tool_calls(request, form_data, user, model, metadata) - assistant_message = await Chats.get_message_by_id_and_message_id(chat_id, message_id) if assistant_message: db_messages.append({k: v for k, v in assistant_message.items() if k in MESSAGE_REPLAY_KEYS}) + context_start_message_id = metadata.get('context_start_message_id') + start_index = next( + (index for index, message in enumerate(db_messages) if message.get('id') == context_start_message_id), 0 + ) + db_messages = db_messages[start_index:] for message in db_messages: output = message.get('output') # reasoning_details can be model/provider-bound, so only replay them @@ -3534,8 +3543,9 @@ async def drain_approved_tool_calls(request, form_data, user, model, metadata) - ): message['output'] = strip_reasoning_details(output) + system_message = get_system_message(form_data.get('messages', [])) form_data['messages'] = process_messages_with_output( - db_messages, + [system_message, *db_messages] if system_message else db_messages, reasoning_format=get_reasoning_format(model), include_file_context=metadata.get('include_file_context', False), ) diff --git a/backend/open_webui/utils/tool_approval.py b/backend/open_webui/utils/tool_approval.py index e2c33e9a74..7d5376bb01 100644 --- a/backend/open_webui/utils/tool_approval.py +++ b/backend/open_webui/utils/tool_approval.py @@ -7,6 +7,8 @@ from sqlalchemy.ext.asyncio import AsyncSession from open_webui.constants import ERROR_MESSAGES from open_webui.env import ENABLE_ADMIN_CHAT_ACCESS from open_webui.models.chats import Chats +from open_webui.models.config import Config +from open_webui.models.users import Users from open_webui.socket.main import get_event_emitter from open_webui.utils.json_codec import JSONCodec @@ -161,8 +163,17 @@ async def build_tool_approval_resume_payload(chat_id: str, message_id: str, chat raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail='Tool call message model is missing.') messages = [] - if params.get('system'): - messages.append({'role': 'system', 'content': params.get('system')}) + system_prompt = params.get('system') + if not system_prompt: + # Mirror the chat UI's system prompt fallback + user = await Users.get_user_by_id(chat.user_id) + ui_settings = (user.settings.ui if user and user.settings else None) or {} + system_prompt = ui_settings.get('system') + if system_prompt is None: + default_interface_settings = await Config.get('ui.default_interface_settings') or {} + system_prompt = default_interface_settings.get('system') + if system_prompt: + messages.append({'role': 'system', 'content': system_prompt}) return { 'stream': params.get('stream_response', True),