From 1be39d2f32166599d3e88fb2946d3a09cc365a25 Mon Sep 17 00:00:00 2001 From: Classic298 <27028174+Classic298@users.noreply.github.com> Date: Wed, 4 Nov 2026 22:44:14 +0100 Subject: [PATCH] fix: prompt cache markers on tool results are dropped before reaching the provider (#33757) Tool results sent through an OpenAI-compatible gateway lose their prompt cache marker right before the request leaves, so Anthropic models behind OpenRouter or LiteLLM never cache the prefix in tool loops. Now the marker is kept on a single text block when a tool result carries one. Tool results without a marker are sent exactly as before. Fixes #33755 --- backend/open_webui/routers/openai.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/backend/open_webui/routers/openai.py b/backend/open_webui/routers/openai.py index 6e1229ed7b..f8323faa8c 100644 --- a/backend/open_webui/routers/openai.py +++ b/backend/open_webui/routers/openai.py @@ -1559,8 +1559,14 @@ async def generate_chat_completion( if not is_responses and 'messages' in payload: for message in payload['messages']: if message.get('role') == 'tool' and isinstance(message.get('content'), list): - message['content'] = ''.join( - part.get('text', '') for part in message['content'] if part.get('type') in ('input_text', 'text') + parts = message['content'] + text = ''.join(part.get('text', '') for part in parts if part.get('type') in ('input_text', 'text')) + cache_control = next( + (part['cache_control'] for part in reversed(parts) if part.get('cache_control')), None + ) + # Prompt-cache markers only work on a content block, so keep the text as one + message['content'] = ( + [{'type': 'text', 'text': text, 'cache_control': cache_control}] if cache_control and text else text ) is_streaming_request = bool(payload.get('stream', False))