diff --git a/libs/partners/anthropic/langchain_anthropic/chat_models.py b/libs/partners/anthropic/langchain_anthropic/chat_models.py index 5b075b3f92f..c62b050fd6e 100644 --- a/libs/partners/anthropic/langchain_anthropic/chat_models.py +++ b/libs/partners/anthropic/langchain_anthropic/chat_models.py @@ -1008,27 +1008,27 @@ class ChatAnthropic(BaseChatModel): system, formatted_messages = _format_messages(messages) - # If cache_control is provided in kwargs, add it to last message - # and content block. - if "cache_control" in kwargs and formatted_messages: - if isinstance(formatted_messages[-1]["content"], list): - formatted_messages[-1]["content"][-1]["cache_control"] = kwargs.pop( - "cache_control" - ) - elif isinstance(formatted_messages[-1]["content"], str): - formatted_messages[-1]["content"] = [ - { - "type": "text", - "text": formatted_messages[-1]["content"], - "cache_control": kwargs.pop("cache_control"), - } - ] - else: - pass - - # If cache_control remains in kwargs, it would be passed as a top-level param - # to the API, but Anthropic expects it nested within a message - _ = kwargs.pop("cache_control", None) + # If cache_control is provided in kwargs, add it to the last message with + # content (Anthropic requires cache_control to be nested within a message + # block). + cache_control = kwargs.pop("cache_control", None) + if cache_control and formatted_messages: + for formatted_message in reversed(formatted_messages): + content = formatted_message.get("content") + if isinstance(content, list) and content: + content[-1]["cache_control"] = cache_control + break + if isinstance(content, str): + formatted_message["content"] = [ + { + "type": "text", + "text": content, + "cache_control": cache_control, + } + ] + break + # If we didn't find a message with content we silently drop the control. + # Anthropic would reject a payload with empty content blocks. payload = { "model": self.model, diff --git a/libs/partners/anthropic/tests/unit_tests/test_chat_models.py b/libs/partners/anthropic/tests/unit_tests/test_chat_models.py index 8c5a155f06b..948daf10c54 100644 --- a/libs/partners/anthropic/tests/unit_tests/test_chat_models.py +++ b/libs/partners/anthropic/tests/unit_tests/test_chat_models.py @@ -1505,6 +1505,7 @@ def test_cache_control_kwarg() -> None: ], }, ] + assert isinstance(messages[-1].content, str) # test no mutation messages = [ HumanMessage("foo"), @@ -1528,6 +1529,23 @@ def test_cache_control_kwarg() -> None: ], }, ] + assert "cache_control" not in messages[-1].content[-1] # test no mutation + + +def test_cache_control_kwarg_skips_empty_messages() -> None: + llm = ChatAnthropic(model=MODEL_NAME) + + messages = [HumanMessage("foo"), AIMessage(content=[])] + payload = llm._get_request_payload(messages, cache_control={"type": "ephemeral"}) + assert payload["messages"] == [ + { + "role": "user", + "content": [ + {"type": "text", "text": "foo", "cache_control": {"type": "ephemeral"}} + ], + }, + {"role": "assistant", "content": []}, + ] def test_context_management_in_payload() -> None: