mirror of
https://github.com/hwchase17/langchain.git
synced 2026-02-21 06:33:41 +00:00
fix(anthropic): hoist cache_control from tool_result content sub-blocks to tool_result level (#35126)
This commit is contained in:
@@ -250,15 +250,31 @@ def _merge_messages(
|
||||
):
|
||||
curr = HumanMessage(curr.content) # type: ignore[misc]
|
||||
else:
|
||||
tool_content = curr.content
|
||||
cache_ctrl = None
|
||||
# Extract cache_control from content blocks and hoist it
|
||||
# to the tool_result level. Anthropic's API does not
|
||||
# support cache_control on tool_result content sub-blocks.
|
||||
if isinstance(tool_content, list):
|
||||
cleaned = []
|
||||
for block in tool_content:
|
||||
if isinstance(block, dict) and "cache_control" in block:
|
||||
cache_ctrl = block["cache_control"]
|
||||
block = {
|
||||
k: v for k, v in block.items() if k != "cache_control"
|
||||
}
|
||||
cleaned.append(block)
|
||||
tool_content = cleaned
|
||||
tool_result: dict = {
|
||||
"type": "tool_result",
|
||||
"content": tool_content,
|
||||
"tool_use_id": curr.tool_call_id,
|
||||
"is_error": curr.status == "error",
|
||||
}
|
||||
if cache_ctrl:
|
||||
tool_result["cache_control"] = cache_ctrl
|
||||
curr = HumanMessage( # type: ignore[misc]
|
||||
[
|
||||
{
|
||||
"type": "tool_result",
|
||||
"content": curr.content,
|
||||
"tool_use_id": curr.tool_call_id,
|
||||
"is_error": curr.status == "error",
|
||||
},
|
||||
],
|
||||
[tool_result],
|
||||
)
|
||||
last = merged[-1] if merged else None
|
||||
if any(
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
import os
|
||||
from collections.abc import Callable
|
||||
from typing import Any, Literal, cast
|
||||
@@ -412,6 +413,91 @@ def test__merge_messages_mutation() -> None:
|
||||
assert messages == original_messages
|
||||
|
||||
|
||||
def test__merge_messages_tool_message_cache_control() -> None:
|
||||
"""Test that cache_control is hoisted from content blocks to tool_result level."""
|
||||
# Test with cache_control in content block
|
||||
messages = [
|
||||
ToolMessage(
|
||||
content=[
|
||||
{
|
||||
"type": "text",
|
||||
"text": "tool output",
|
||||
"cache_control": {"type": "ephemeral"},
|
||||
}
|
||||
],
|
||||
tool_call_id="1",
|
||||
)
|
||||
]
|
||||
original_messages = [copy.deepcopy(m) for m in messages]
|
||||
expected = [
|
||||
HumanMessage(
|
||||
[
|
||||
{
|
||||
"type": "tool_result",
|
||||
"content": [{"type": "text", "text": "tool output"}],
|
||||
"tool_use_id": "1",
|
||||
"is_error": False,
|
||||
"cache_control": {"type": "ephemeral"},
|
||||
}
|
||||
]
|
||||
)
|
||||
]
|
||||
actual = _merge_messages(messages)
|
||||
assert expected == actual
|
||||
# Verify no mutation
|
||||
assert messages == original_messages
|
||||
|
||||
# Test with multiple content blocks, cache_control on last one
|
||||
messages = [
|
||||
ToolMessage(
|
||||
content=[
|
||||
{"type": "text", "text": "first output"},
|
||||
{
|
||||
"type": "text",
|
||||
"text": "second output",
|
||||
"cache_control": {"type": "ephemeral"},
|
||||
},
|
||||
],
|
||||
tool_call_id="2",
|
||||
)
|
||||
]
|
||||
expected = [
|
||||
HumanMessage(
|
||||
[
|
||||
{
|
||||
"type": "tool_result",
|
||||
"content": [
|
||||
{"type": "text", "text": "first output"},
|
||||
{"type": "text", "text": "second output"},
|
||||
],
|
||||
"tool_use_id": "2",
|
||||
"is_error": False,
|
||||
"cache_control": {"type": "ephemeral"},
|
||||
}
|
||||
]
|
||||
)
|
||||
]
|
||||
actual = _merge_messages(messages)
|
||||
assert expected == actual
|
||||
|
||||
# Test without cache_control
|
||||
messages = [ToolMessage(content="simple output", tool_call_id="3")]
|
||||
expected = [
|
||||
HumanMessage(
|
||||
[
|
||||
{
|
||||
"type": "tool_result",
|
||||
"content": "simple output",
|
||||
"tool_use_id": "3",
|
||||
"is_error": False,
|
||||
}
|
||||
]
|
||||
)
|
||||
]
|
||||
actual = _merge_messages(messages)
|
||||
assert expected == actual
|
||||
|
||||
|
||||
def test__format_image() -> None:
|
||||
url = "dummyimage.com/600x400/000/fff"
|
||||
with pytest.raises(ValueError):
|
||||
|
||||
Reference in New Issue
Block a user