Skip to content

Commit 039c8b0

Browse files
committed
fix: serialize non-standard FunctionResponse dicts in AnthropicLlm
Rebased onto latest main, resolved conflicts with upstream json.dumps improvement. Keeps both upstream's json.dumps serialization for standard results AND the fallback path for non-standard response dicts (e.g. SkillToolset).
1 parent f973673 commit 039c8b0

2 files changed

Lines changed: 37 additions & 4 deletions

File tree

src/google/adk/models/anthropic_llm.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -147,10 +147,9 @@ def part_to_message_block(
147147
else:
148148
content = str(result)
149149
elif response_data:
150-
# Fallback: serialize the entire response dict as JSON so that tools
151-
# returning arbitrary key structures (e.g. load_skill returning
152-
# {"skill_name", "instructions", "frontmatter"}) are not silently
153-
# dropped.
150+
# Fallback: serialize the entire non-standard response dict as JSON so
151+
# tools returning arbitrary key structures (e.g. SkillToolset with
152+
# skill_name/instructions/frontmatter) are not silently dropped.
154153
content = json.dumps(response_data)
155154

156155
return anthropic_types.ToolResultBlockParam(

tests/unittests/models/test_anthropic_llm.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -741,6 +741,30 @@ def test_part_to_message_block_with_multiple_content_items():
741741
assert result["content"] == "First part\nSecond part"
742742

743743

744+
def test_part_to_message_block_with_non_standard_response():
745+
"""Test that part_to_message_block serializes non-standard response dicts.
746+
747+
Regression test for https://github.com/google/adk-python/issues/4779.
748+
SkillToolset returns dicts like {"skill_name": ..., "instructions": ...}
749+
that don't contain "content" or "result" keys. These were silently
750+
dropped (empty string), causing Claude to never see the tool output.
751+
"""
752+
import json
753+
754+
from google.adk.models.anthropic_llm import part_to_message_block
755+
756+
skill_response = {
757+
"skill_name": "search_docs",
758+
"instructions": "Use the search API to find documents.",
759+
"frontmatter": {"version": "1.0"},
760+
}
761+
part = types.Part.from_function_response(
762+
name="load_skill",
763+
response=skill_response,
764+
)
765+
part.function_response.id = "test_skill_id"
766+
767+
744768
def test_part_to_message_block_with_pdf_document():
745769
"""Test that part_to_message_block handles PDF document parts."""
746770
pdf_data = b"%PDF-1.4 fake pdf content"
@@ -751,6 +775,16 @@ def test_part_to_message_block_with_pdf_document():
751775
result = part_to_message_block(part)
752776

753777
assert isinstance(result, dict)
778+
assert result["tool_use_id"] == "test_skill_id"
779+
assert result["type"] == "tool_result"
780+
assert not result["is_error"]
781+
# Content must be non-empty and contain the original data
782+
parsed = json.loads(result["content"])
783+
assert parsed["skill_name"] == "search_docs"
784+
assert parsed["instructions"] == "Use the search API to find documents."
785+
assert parsed["frontmatter"] == {"version": "1.0"}
786+
787+
754788
assert result["type"] == "document"
755789
assert result["source"]["type"] == "base64"
756790
assert result["source"]["media_type"] == "application/pdf"

0 commit comments

Comments
 (0)