diff --git a/src/google/adk/models/anthropic_llm.py b/src/google/adk/models/anthropic_llm.py index a14c767f23..db8a85099c 100644 --- a/src/google/adk/models/anthropic_llm.py +++ b/src/google/adk/models/anthropic_llm.py @@ -124,20 +124,26 @@ def part_to_message_block( content = "" response_data = part.function_response.response - # Handle response with content array - if "content" in response_data and response_data["content"]: - content_items = [] - for item in response_data["content"]: - if isinstance(item, dict): - # Handle text content blocks - if item.get("type") == "text" and "text" in item: - content_items.append(item["text"]) + # Handle response with content payloads. + if "content" in response_data: + content_value = response_data["content"] + if isinstance(content_value, list): + content_items = [] + for item in content_value: + if isinstance(item, dict): + # Handle text content blocks + if item.get("type") == "text" and "text" in item: + content_items.append(item["text"]) + else: + # Handle other structured content + content_items.append(str(item)) else: - # Handle other structured content content_items.append(str(item)) - else: - content_items.append(str(item)) - content = "\n".join(content_items) if content_items else "" + content = "\n".join(content_items) if content_items else "" + elif isinstance(content_value, dict): + content = json.dumps(content_value) + elif content_value is not None: + content = str(content_value) # We serialize to str here # SDK ref: anthropic.types.tool_result_block_param # https://github.com/anthropics/anthropic-sdk-python/blob/main/src/anthropic/types/tool_result_block_param.py diff --git a/tests/unittests/models/test_anthropic_llm.py b/tests/unittests/models/test_anthropic_llm.py index fb44d5c8e7..520d1eb216 100644 --- a/tests/unittests/models/test_anthropic_llm.py +++ b/tests/unittests/models/test_anthropic_llm.py @@ -776,6 +776,52 @@ def test_part_to_message_block_with_multiple_content_items(): assert result["content"] == "First part\nSecond part" +def test_part_to_message_block_with_string_content(): + """String content should stay intact instead of being split per character.""" + from google.adk.models.anthropic_llm import part_to_message_block + + response_part = types.Part.from_function_response( + name="load_skill_resource", + response={ + "skill_name": "my_skill", + "file_path": "references/doc.md", + "content": "Hello", + }, + ) + response_part.function_response.id = "test_id_790" + + result = part_to_message_block(response_part) + + assert isinstance(result, dict) + assert result["tool_use_id"] == "test_id_790" + assert result["type"] == "tool_result" + assert not result["is_error"] + assert result["content"] == "Hello" + + +def test_part_to_message_block_with_empty_string_content(): + """An explicit empty content payload should stay empty.""" + from google.adk.models.anthropic_llm import part_to_message_block + + response_part = types.Part.from_function_response( + name="load_skill_resource", + response={ + "skill_name": "my_skill", + "file_path": "references/doc.md", + "content": "", + }, + ) + response_part.function_response.id = "test_id_791" + + result = part_to_message_block(response_part) + + assert isinstance(result, dict) + assert result["tool_use_id"] == "test_id_791" + assert result["type"] == "tool_result" + assert not result["is_error"] + assert result["content"] == "" + + def test_part_to_message_block_with_pdf_document(): """Test that part_to_message_block handles PDF document parts.""" pdf_data = b"%PDF-1.4 fake pdf content"