From 4dc7a14f72d870264f7c53779d6f03b867bcc394 Mon Sep 17 00:00:00 2001 From: Harshit Jain Date: Fri, 9 Jan 2026 20:33:52 +0530 Subject: [PATCH] fix(dashscope): preserve cache_control in messages #18165 --- litellm/llms/dashscope/chat/transformation.py | 47 +++++++------------ .../test_dashscope_chat_transformation.py | 37 +++++++++++++++ 2 files changed, 55 insertions(+), 29 deletions(-) diff --git a/litellm/llms/dashscope/chat/transformation.py b/litellm/llms/dashscope/chat/transformation.py index 155d8c9ec27..5a7abf11e55 100644 --- a/litellm/llms/dashscope/chat/transformation.py +++ b/litellm/llms/dashscope/chat/transformation.py @@ -2,48 +2,37 @@ Translates from OpenAI's `/v1/chat/completions` to DashScope's `/v1/chat/completions` """ -from typing import Any, Coroutine, List, Literal, Optional, Tuple, Union, overload +from typing import TYPE_CHECKING, List, Optional, Tuple -from litellm.litellm_core_utils.prompt_templates.common_utils import ( - handle_messages_with_content_list_to_str_conversion, -) from litellm.secret_managers.main import get_secret_str from litellm.types.llms.openai import AllMessageValues from ...openai.chat.gpt_transformation import OpenAIGPTConfig +if TYPE_CHECKING: + from litellm.types.llms.openai import ChatCompletionToolParam + class DashScopeChatConfig(OpenAIGPTConfig): - @overload - def _transform_messages( - self, messages: List[AllMessageValues], model: str, is_async: Literal[True] - ) -> Coroutine[Any, Any, List[AllMessageValues]]: - ... + """ + DashScope configuration. - @overload - def _transform_messages( + DashScope supports content in list format with cache_control metadata. + See: https://github.com/BerriAI/litellm/issues/18165 + """ + + def remove_cache_control_flag_from_messages_and_tools( self, - messages: List[AllMessageValues], model: str, - is_async: Literal[False] = False, - ) -> List[AllMessageValues]: - ... + messages: List[AllMessageValues], + tools: Optional[List["ChatCompletionToolParam"]] = None, + ) -> Tuple[List[AllMessageValues], Optional[List["ChatCompletionToolParam"]]]: + """ + DashScope supports cache_control, so we preserve it instead of removing it. - def _transform_messages( - self, messages: List[AllMessageValues], model: str, is_async: bool = False - ) -> Union[List[AllMessageValues], Coroutine[Any, Any, List[AllMessageValues]]]: + Override parent behavior that strips cache_control for OpenAI compatibility. """ - DashScope does not support content in list format. - """ - messages = handle_messages_with_content_list_to_str_conversion(messages) - if is_async: - return super()._transform_messages( - messages=messages, model=model, is_async=True - ) - else: - return super()._transform_messages( - messages=messages, model=model, is_async=False - ) + return messages, tools def _get_openai_compatible_provider_info( self, api_base: Optional[str], api_key: Optional[str] 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 ff2302749f1..4b23735296a 100644 --- a/tests/test_litellm/llms/dashscope/test_dashscope_chat_transformation.py +++ b/tests/test_litellm/llms/dashscope/test_dashscope_chat_transformation.py @@ -111,3 +111,40 @@ class TestDashScopeConfig: # Check for specific content in the response assert "```python" in response.choices[0].message.content assert "Hey from LiteLLM" in response.choices[0].message.content + + def test_dashscope_preserves_cache_control(self): + """ + Test that DashScope preserves cache_control metadata in messages. + + Regression test for: https://github.com/BerriAI/litellm/issues/18165 + DashScope now supports cache_control, so we should NOT strip it. + """ + config = DashScopeChatConfig() + + messages_with_cache = [ + { + "role": "user", + "content": [ + { + "type": "text", + "text": "Long document content here...", + "cache_control": {"type": "ephemeral"}, + } + ], + } + ] + + # This should preserve cache_control (not strip it like OpenAI does) + ( + transformed_messages, + tools, + ) = config.remove_cache_control_flag_from_messages_and_tools( + model="qwen-turbo", + messages=messages_with_cache, + tools=None, + ) + + # Verify cache_control is preserved + assert transformed_messages[0]["content"][0]["cache_control"] == { + "type": "ephemeral" + }