fix: stringify Anthropic typeless blocks as JSON

This commit is contained in:
Xubin Ren
2026-06-27 16:47:59 +08:00
parent efb792ff24
commit 00a7de0171
2 changed files with 24 additions and 4 deletions
+13 -2
View File
@@ -4,6 +4,7 @@ from __future__ import annotations
import asyncio
import hashlib
import json
import re
import secrets
import string
@@ -280,7 +281,10 @@ class AnthropicProvider(LLMProvider):
# Anthropic requires every content block to declare a "type".
# A tool that returned a bare dict lands here; coerce it to
# a text block instead of emitting one that the API rejects.
blocks.append({"type": "text", "text": str(item)})
blocks.append({
"type": "text",
"text": AnthropicProvider._stringify_typeless_block(item),
})
else:
blocks.append(item)
else:
@@ -324,11 +328,18 @@ class AnthropicProvider(LLMProvider):
# A tool that returned a bare dict (or a list of dicts) lands
# here; coerce it to a text block instead of emitting a block
# the API rejects with "content.0.type: Field required".
result.append({"type": "text", "text": str(item)})
result.append({
"type": "text",
"text": AnthropicProvider._stringify_typeless_block(item),
})
continue
result.append(item)
return result or "(empty)"
@staticmethod
def _stringify_typeless_block(block: dict[str, Any]) -> str:
return json.dumps(block, ensure_ascii=False, sort_keys=True, default=str)
@staticmethod
def _convert_image_block(block: dict[str, Any]) -> dict[str, Any] | None:
"""Convert OpenAI image_url block to Anthropic image block."""
+11 -2
View File
@@ -70,7 +70,7 @@ def test_convert_user_content_coerces_typeless_dict():
{"foo": "bar"},
{"type": "text", "text": "ok"},
])
assert result[0] == {"type": "text", "text": str({"foo": "bar"})}
assert result[0] == {"type": "text", "text": '{"foo": "bar"}'}
assert result[1] == {"type": "text", "text": "ok"}
@@ -81,7 +81,16 @@ def test_convert_user_content_coerces_mixed_typeless():
{"key": "val"},
])
assert result[0] == {"type": "text", "text": "42"}
assert result[1] == {"type": "text", "text": str({"key": "val"})}
assert result[1] == {"type": "text", "text": '{"key": "val"}'}
def test_assistant_blocks_coerce_typeless_dict_to_json_text():
blocks = AnthropicProvider._assistant_blocks({
"role": "assistant",
"content": [{"answer": "ok", "count": 2}],
})
assert blocks == [{"type": "text", "text": '{"answer": "ok", "count": 2}'}]
def test_convert_assistant_message_repairs_history_tool_arguments():