fix(anthropic): sanitize tool use block when taking directly from content (#32574)

This commit is contained in:
ccurme 2025-08-18 10:06:57 -03:00 committed by GitHub
parent 791d309c06
commit b8cdbc4eca
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 62 additions and 6 deletions

View File

@ -365,8 +365,23 @@ def _format_messages(
), ),
) )
else: else:
block.pop("text", None) if tool_input := block.get("input"):
content.append(block) args = tool_input
elif "partial_json" in block:
try:
args = json.loads(block["partial_json"] or "{}")
except json.JSONDecodeError:
args = {}
else:
args = {}
content.append(
_AnthropicToolUse(
type="tool_use",
name=block["name"],
input=args,
id=block["id"],
)
)
elif block["type"] in ("server_tool_use", "mcp_tool_use"): elif block["type"] in ("server_tool_use", "mcp_tool_use"):
formatted_block = { formatted_block = {
k: v k: v

View File

@ -597,6 +597,47 @@ def test__format_messages_with_tool_calls() -> None:
assert expected == actual assert expected == actual
def test__format_tool_use_block() -> None:
# Test we correctly format tool_use blocks when there is no corresponding tool_call.
message = AIMessage(
[
{
"type": "tool_use",
"name": "foo_1",
"id": "1",
"input": {"bar_1": "baz_1"},
},
{
"type": "tool_use",
"name": "foo_2",
"id": "2",
"input": {},
"partial_json": '{"bar_2": "baz_2"}',
"index": 1,
},
]
)
result = _format_messages([message])
expected = {
"role": "assistant",
"content": [
{
"type": "tool_use",
"name": "foo_1",
"id": "1",
"input": {"bar_1": "baz_1"},
},
{
"type": "tool_use",
"name": "foo_2",
"id": "2",
"input": {"bar_2": "baz_2"},
},
],
}
assert result == (None, [expected])
def test__format_messages_with_str_content_and_tool_calls() -> None: def test__format_messages_with_str_content_and_tool_calls() -> None:
system = SystemMessage("fuzz") # type: ignore[misc] system = SystemMessage("fuzz") # type: ignore[misc]
human = HumanMessage("foo") # type: ignore[misc] human = HumanMessage("foo") # type: ignore[misc]

View File

@ -1,5 +1,5 @@
version = 1 version = 1
revision = 3 revision = 2
requires-python = ">=3.9" requires-python = ">=3.9"
resolution-markers = [ resolution-markers = [
"python_full_version >= '3.13' and platform_python_implementation == 'PyPy'", "python_full_version >= '3.13' and platform_python_implementation == 'PyPy'",
@ -23,7 +23,7 @@ wheels = [
[[package]] [[package]]
name = "anthropic" name = "anthropic"
version = "0.60.0" version = "0.64.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "anyio" }, { name = "anyio" },
@ -34,9 +34,9 @@ dependencies = [
{ name = "sniffio" }, { name = "sniffio" },
{ name = "typing-extensions" }, { name = "typing-extensions" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/4e/03/3334921dc54ed822b3dd993ae72d823a7402588521bbba3e024b3333a1fd/anthropic-0.60.0.tar.gz", hash = "sha256:a22ba187c6f4fd5afecb2fc913b960feccf72bc0d25c1b7ce0345e87caede577", size = 425983, upload-time = "2025-07-28T19:53:47.685Z" } sdist = { url = "https://files.pythonhosted.org/packages/d8/4f/f2b880cba1a76f3acc7d5eb2ae217632eac1b8cef5ed3027493545c59eba/anthropic-0.64.0.tar.gz", hash = "sha256:3d496c91a63dff64f451b3e8e4b238a9640bf87b0c11d0b74ddc372ba5a3fe58", size = 427893, upload-time = "2025-08-13T17:09:49.915Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/da/bb/d84f287fb1c217b30c328af987cf8bbe3897edf0518dcc5fa39412f794ec/anthropic-0.60.0-py3-none-any.whl", hash = "sha256:65ad1f088a960217aaf82ba91ff743d6c89e9d811c6d64275b9a7c59ee9ac3c6", size = 293116, upload-time = "2025-07-28T19:53:45.944Z" }, { url = "https://files.pythonhosted.org/packages/a9/b2/2d268bcd5d6441df9dc0ebebc67107657edb8b0150d3fda1a5b81d1bec45/anthropic-0.64.0-py3-none-any.whl", hash = "sha256:6f5f7d913a6a95eb7f8e1bda4e75f76670e8acd8d4cd965e02e2a256b0429dd1", size = 297244, upload-time = "2025-08-13T17:09:47.908Z" },
] ]
[[package]] [[package]]