diff --git a/libs/partners/anthropic/langchain_anthropic/chat_models.py b/libs/partners/anthropic/langchain_anthropic/chat_models.py index 410fee9a7dd..7629b0b5c63 100644 --- a/libs/partners/anthropic/langchain_anthropic/chat_models.py +++ b/libs/partners/anthropic/langchain_anthropic/chat_models.py @@ -365,8 +365,23 @@ def _format_messages( ), ) else: - block.pop("text", None) - content.append(block) + if tool_input := block.get("input"): + 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"): formatted_block = { k: v 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 fee1125ce9e..0084d2740ac 100644 --- a/libs/partners/anthropic/tests/unit_tests/test_chat_models.py +++ b/libs/partners/anthropic/tests/unit_tests/test_chat_models.py @@ -597,6 +597,47 @@ def test__format_messages_with_tool_calls() -> None: 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: system = SystemMessage("fuzz") # type: ignore[misc] human = HumanMessage("foo") # type: ignore[misc] diff --git a/libs/partners/anthropic/uv.lock b/libs/partners/anthropic/uv.lock index 47e9d28bccc..3f31dfc2c20 100644 --- a/libs/partners/anthropic/uv.lock +++ b/libs/partners/anthropic/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 3 +revision = 2 requires-python = ">=3.9" resolution-markers = [ "python_full_version >= '3.13' and platform_python_implementation == 'PyPy'", @@ -23,7 +23,7 @@ wheels = [ [[package]] name = "anthropic" -version = "0.60.0" +version = "0.64.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -34,9 +34,9 @@ dependencies = [ { name = "sniffio" }, { 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 = [ - { 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]]