From fa63c09c2c463261f1ced35019a9db86ba78d359 Mon Sep 17 00:00:00 2001 From: mxchen Date: Fri, 27 Feb 2026 11:48:21 +0800 Subject: [PATCH] fix: reserve cache_control in DashScopeChatConfig._transform_messages --- litellm/llms/dashscope/chat/transformation.py | 13 +++- .../test_dashscope_chat_transformation.py | 73 +++++++++++++++++++ 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/litellm/llms/dashscope/chat/transformation.py b/litellm/llms/dashscope/chat/transformation.py index cc5cf991826..e93503c5e8a 100644 --- a/litellm/llms/dashscope/chat/transformation.py +++ b/litellm/llms/dashscope/chat/transformation.py @@ -5,12 +5,23 @@ Translates from OpenAI's `/v1/chat/completions` to DashScope's `/v1/chat/complet from typing import Any, Coroutine, List, Literal, Optional, Tuple, Union, overload from litellm.secret_managers.main import get_secret_str -from litellm.types.llms.openai import AllMessageValues +from litellm.types.llms.openai import AllMessageValues, ChatCompletionToolParam from ...openai.chat.gpt_transformation import OpenAIGPTConfig class DashScopeChatConfig(OpenAIGPTConfig): + def remove_cache_control_flag_from_messages_and_tools( + self, + model: str, + messages: List[AllMessageValues], + tools: Optional[List[ChatCompletionToolParam]] = None, + ) -> Tuple[List[AllMessageValues], Optional[List[ChatCompletionToolParam]]]: + """ + DashScope supports cache_control - don't strip it. + """ + return messages, tools + @overload def _transform_messages( self, messages: List[AllMessageValues], model: str, is_async: Literal[True] diff --git a/tests/test_litellm/llms/dashscope/test_dashscope_chat_transformation.py b/tests/test_litellm/llms/dashscope/test_dashscope_chat_transformation.py index b5f656c71f8..950b46ae112 100644 --- a/tests/test_litellm/llms/dashscope/test_dashscope_chat_transformation.py +++ b/tests/test_litellm/llms/dashscope/test_dashscope_chat_transformation.py @@ -113,6 +113,79 @@ class TestDashScopeConfig: assert "```python" in response.choices[0].message.content assert "Hey from LiteLLM" in response.choices[0].message.content + def test_dashscope_preserves_cache_control_in_messages(self): + """ + Test that DashScopeChatConfig does not strip cache_control from messages. + DashScope supports cache_control, so it should be preserved unlike the + default OpenAIGPTConfig behavior which strips it. + """ + config = DashScopeChatConfig() + + messages: list[AllMessageValues] = [ + { + "role": "system", + "content": "You are a helpful assistant.", + "cache_control": {"type": "ephemeral"}, + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "Hello", + "cache_control": {"type": "ephemeral"}, + }, + ], + }, + ] + + result_messages, result_tools = ( + config.remove_cache_control_flag_from_messages_and_tools( + model="qwen-turbo", + messages=messages, + tools=None, + ) + ) + + assert result_messages[0].get("cache_control") == {"type": "ephemeral"} + assert result_messages[1]["content"][0].get("cache_control") == { + "type": "ephemeral" + } + assert result_tools is None + + def test_dashscope_preserves_cache_control_in_tools(self): + """ + Test that DashScopeChatConfig preserves cache_control in tools. + """ + config = DashScopeChatConfig() + + messages: list[AllMessageValues] = [ + {"role": "user", "content": "Hello"}, + ] + + tools = [ + { + "type": "function", + "function": { + "name": "get_weather", + "description": "Get weather", + "parameters": {"type": "object", "properties": {}}, + }, + "cache_control": {"type": "ephemeral"}, + }, + ] + + result_messages, result_tools = ( + config.remove_cache_control_flag_from_messages_and_tools( + model="qwen-turbo", + messages=messages, + tools=tools, + ) + ) + + assert result_tools is not None + assert result_tools[0].get("cache_control") == {"type": "ephemeral"} + def test_dashscope_no_longer_transforms_content_list(self): """ Test that DashScopeChatConfig does not transform content lists to strings.