Skip to content

Commit db815f8

Browse files
giulio-leoneCopilot
andcommitted
fix: serialize non-standard FunctionResponse dicts in AnthropicLlm
part_to_message_block() only handled response dicts containing 'content' or 'result' keys. Any other dict structure (e.g. SkillToolset returning {"skill_name": ..., "instructions": ...}) fell through to an empty string, causing Claude to never see the tool output. Add json.dumps() fallback for non-standard response dicts so all structured data reaches the model. Fixes #4779 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 8ddddc0 commit db815f8

2 files changed

Lines changed: 41 additions & 0 deletions

File tree

src/google/adk/models/anthropic_llm.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import base64
2020
from functools import cached_property
21+
import json
2122
import logging
2223
import os
2324
from typing import Any
@@ -121,6 +122,10 @@ def part_to_message_block(
121122
# ToolResultBlockParam content doesn't support list of dict. Converting
122123
# to str to prevent anthropic.BadRequestError from being thrown.
123124
content = str(response_data["result"])
125+
elif response_data:
126+
# Fallback: serialize any non-standard response dict (e.g. SkillToolset
127+
# returns {"skill_name": ..., "instructions": ..., "frontmatter": ...})
128+
content = json.dumps(response_data)
124129

125130
return anthropic_types.ToolResultBlockParam(
126131
tool_use_id=part.function_response.id or "",

tests/unittests/models/test_anthropic_llm.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,42 @@ def test_part_to_message_block_with_multiple_content_items():
522522
assert result["content"] == "First part\nSecond part"
523523

524524

525+
def test_part_to_message_block_with_non_standard_response():
526+
"""Test that part_to_message_block serializes non-standard response dicts.
527+
528+
Regression test for https://github.com/google/adk-python/issues/4779.
529+
SkillToolset returns dicts like {"skill_name": ..., "instructions": ...}
530+
that don't contain "content" or "result" keys. These were silently
531+
dropped (empty string), causing Claude to never see the tool output.
532+
"""
533+
import json
534+
535+
from google.adk.models.anthropic_llm import part_to_message_block
536+
537+
skill_response = {
538+
"skill_name": "search_docs",
539+
"instructions": "Use the search API to find documents.",
540+
"frontmatter": {"version": "1.0"},
541+
}
542+
part = types.Part.from_function_response(
543+
name="load_skill",
544+
response=skill_response,
545+
)
546+
part.function_response.id = "test_skill_id"
547+
548+
result = part_to_message_block(part)
549+
550+
assert isinstance(result, dict)
551+
assert result["tool_use_id"] == "test_skill_id"
552+
assert result["type"] == "tool_result"
553+
assert not result["is_error"]
554+
# Content must be non-empty and contain the original data
555+
parsed = json.loads(result["content"])
556+
assert parsed["skill_name"] == "search_docs"
557+
assert parsed["instructions"] == "Use the search API to find documents."
558+
assert parsed["frontmatter"] == {"version": "1.0"}
559+
560+
525561
content_to_message_param_test_cases = [
526562
(
527563
"user_role_with_text_and_image",

0 commit comments

Comments
 (0)