From d3cfcd801e290299c83ea7b538d5e6c0828b1ba4 Mon Sep 17 00:00:00 2001 From: Classic298 <27028174+Classic298@users.noreply.github.com> Date: Mon, 27 Jul 2026 08:13:28 +0200 Subject: [PATCH] fix: preserve system prompt across tool calls when memories are enabled (#26857) * fix: preserve system prompt across tool calls when memories are enabled The native tool-call loop runs generate_chat_completion with bypass_system_prompt=True, so the provider layer does not re-apply the model's default system prompt on tool-call iterations. It relies instead on metadata['system_prompt'], captured in process_chat_payload, to carry the full system prompt forward and restore it after RAG injection. That capture read the model default system prompt from form_data['params']['system'], but apply_params_to_form_data had already popped 'params' from form_data, so model_system_prompt was always empty. metadata['system_prompt'] therefore only captured whatever was already materialized in the messages. With memories enabled, that is the injected system message, so tool-call requests were restored with memory-only system content and the model's system prompt was dropped. Without memories there was no system message to capture at all. Capture the model default system prompt from form_data['params'] before apply_params_to_form_data pops it, and use that value when building metadata['system_prompt']. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_014Z4L51mvxDJCx1EDxP2vFN * refactor: condense system prompt capture comment to a single line Replace the four-line explanation above the model_system_prompt capture with a one-line note. The variable name and the surrounding code already convey what happens; the comment only needs to state why the capture sits before apply_params_to_form_data. --------- Co-authored-by: Claude --- backend/open_webui/utils/middleware.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index be87d1c1ee..ab92882acc 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -2268,6 +2268,9 @@ async def process_chat_payload(request, form_data, user, metadata, model): form_data['model'] = selected_model_id metadata['selected_model_id'] = selected_model_id + # Captured before apply_params_to_form_data pops 'params'; feeds metadata['system_prompt'] below + model_system_prompt = (form_data.get('params') or {}).get('system') + form_data = apply_params_to_form_data(form_data, model) log.debug(f'form_data: {form_data}') @@ -2920,13 +2923,17 @@ async def process_chat_payload(request, form_data, user, metadata, model): # than a snapshot that already has the RAG template baked in. system_message = get_system_message(form_data['messages']) system_content = get_content_from_message(system_message) if system_message else '' - model_system_prompt = await resolve_system_prompt( - (form_data.get('params') or {}).get('system'), + resolved_model_system_prompt = await resolve_system_prompt( + model_system_prompt, metadata, user, ) - if model_system_prompt: - system_content = f'{model_system_prompt}\n{system_content}' if system_content else model_system_prompt + if resolved_model_system_prompt: + system_content = ( + f'{resolved_model_system_prompt}\n{system_content}' + if system_content + else resolved_model_system_prompt + ) metadata['system_prompt'] = system_content or None metadata['user_prompt'] = get_last_user_message(form_data['messages']) metadata['sources'] = sources[:] if sources else []