Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/google/adk/models/anthropic_llm.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,10 @@ def part_to_message_block(
content = json.dumps(result)
else:
content = str(result)
elif response_data:
# Fallback: serialize any non-standard response dict (e.g. SkillToolset
# returns {"skill_name": ..., "instructions": ..., "frontmatter": ...})
content = json.dumps(response_data)

return anthropic_types.ToolResultBlockParam(
tool_use_id=part.function_response.id or "",
Expand Down
34 changes: 34 additions & 0 deletions tests/unittests/models/test_anthropic_llm.py
Original file line number Diff line number Diff line change
Expand Up @@ -741,6 +741,30 @@ def test_part_to_message_block_with_multiple_content_items():
assert result["content"] == "First part\nSecond part"


def test_part_to_message_block_with_non_standard_response():
"""Test that part_to_message_block serializes non-standard response dicts.

Regression test for https://github.com/google/adk-python/issues/4779.
SkillToolset returns dicts like {"skill_name": ..., "instructions": ...}
that don't contain "content" or "result" keys. These were silently
dropped (empty string), causing Claude to never see the tool output.
"""
import json

from google.adk.models.anthropic_llm import part_to_message_block

skill_response = {
"skill_name": "search_docs",
"instructions": "Use the search API to find documents.",
"frontmatter": {"version": "1.0"},
}
part = types.Part.from_function_response(
name="load_skill",
response=skill_response,
)
part.function_response.id = "test_skill_id"


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"
Expand All @@ -751,6 +775,16 @@ def test_part_to_message_block_with_pdf_document():
result = part_to_message_block(part)

assert isinstance(result, dict)
assert result["tool_use_id"] == "test_skill_id"
assert result["type"] == "tool_result"
assert not result["is_error"]
# Content must be non-empty and contain the original data
parsed = json.loads(result["content"])
assert parsed["skill_name"] == "search_docs"
assert parsed["instructions"] == "Use the search API to find documents."
assert parsed["frontmatter"] == {"version": "1.0"}


assert result["type"] == "document"
assert result["source"]["type"] == "base64"
assert result["source"]["media_type"] == "application/pdf"
Expand Down