Skip to content

Commit 02cd094

Browse files
fix(anthropic): handle plain string content in part_to_message_block
When a tool returns a response where the "content" key holds a plain string, part_to_message_block() was iterating over it directly. Since strings are iterable in Python, the loop produced one character per iteration instead of the full text. LoadSkillResourceTool is a concrete case: it returns a response dict with a "content" key that is a string, not a list. The fix adds an isinstance check before the loop. When content is a string it is used directly. When it is a list, the existing join logic runs unchanged. A unit test covers both paths. Fixes #5358
1 parent 69fa777 commit 02cd094

2 files changed

Lines changed: 37 additions & 11 deletions

File tree

src/google/adk/models/anthropic_llm.py

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -197,20 +197,23 @@ def part_to_message_block(
197197
content = ""
198198
response_data = part.function_response.response
199199

200-
# Handle response with content array
200+
# Handle response with content array or plain string
201201
if "content" in response_data and response_data["content"]:
202-
content_items = []
203-
for item in response_data["content"]:
204-
if isinstance(item, dict):
205-
# Handle text content blocks
206-
if item.get("type") == "text" and "text" in item:
207-
content_items.append(item["text"])
202+
if isinstance(response_data["content"], str):
203+
content = response_data["content"]
204+
else:
205+
content_items = []
206+
for item in response_data["content"]:
207+
if isinstance(item, dict):
208+
# Handle text content blocks
209+
if item.get("type") == "text" and "text" in item:
210+
content_items.append(item["text"])
211+
else:
212+
# Handle other structured content
213+
content_items.append(str(item))
208214
else:
209-
# Handle other structured content
210215
content_items.append(str(item))
211-
else:
212-
content_items.append(str(item))
213-
content = "\n".join(content_items) if content_items else ""
216+
content = "\n".join(content_items) if content_items else ""
214217
# We serialize to str here
215218
# SDK ref: anthropic.types.tool_result_block_param
216219
# https://github.com/anthropics/anthropic-sdk-python/blob/main/src/anthropic/types/tool_result_block_param.py

tests/unittests/models/test_anthropic_llm.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -776,6 +776,29 @@ def test_part_to_message_block_with_multiple_content_items():
776776
assert result["content"] == "First part\nSecond part"
777777

778778

779+
def test_part_to_message_block_with_string_content():
780+
"""Test that a plain string in the content key is passed through as-is.
781+
782+
LoadSkillResourceTool returns {"content": "<file text>"} where content is
783+
a string, not a list. Iterating over a string yields individual characters,
784+
so without an isinstance check the result would be "H\ne\nl\nl\no" instead
785+
of "Hello".
786+
"""
787+
part = types.Part.from_function_response(
788+
name="load_skill_resource",
789+
response={
790+
"skill_name": "my-skill",
791+
"file_path": "references/doc.md",
792+
"content": "Hello world",
793+
},
794+
)
795+
part.function_response.id = "test_id_str"
796+
797+
result = part_to_message_block(part)
798+
799+
assert result["content"] == "Hello world"
800+
801+
779802
def test_part_to_message_block_with_pdf_document():
780803
"""Test that part_to_message_block handles PDF document parts."""
781804
pdf_data = b"%PDF-1.4 fake pdf content"

0 commit comments

Comments
 (0)