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
<memory_context> 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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
This commit is contained in:
Classic298 2026-07-27 08:13:28 +02:00 committed by GitHub
parent f91ac068d0
commit d3cfcd801e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -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 []