diff --git a/litellm/llms/base_llm/base_utils.py b/litellm/llms/base_llm/base_utils.py index be2cf17b8ae..5466ac71291 100644 --- a/litellm/llms/base_llm/base_utils.py +++ b/litellm/llms/base_llm/base_utils.py @@ -229,7 +229,9 @@ def _is_decoded_json_object( def decoded_answer_object(text: str) -> Mapping[str, AnswerValue] | None: try: - decoded: Final[object] = json.loads(text, parse_float=_source_float, parse_int=_source_int) + decoded: Final = _DECODED_JSON.validate_python( + json.loads(text, parse_float=_source_float, parse_int=_source_int) + ) except (json.JSONDecodeError, RecursionError): return None return decoded if _is_decoded_json_object(decoded) else None diff --git a/tests/unit/llms/anthropic/chat/test_anthropic_chat_handler.py b/tests/unit/llms/anthropic/chat/test_anthropic_chat_handler.py index 4d08db2ff06..c366c21bdca 100644 --- a/tests/unit/llms/anthropic/chat/test_anthropic_chat_handler.py +++ b/tests/unit/llms/anthropic/chat/test_anthropic_chat_handler.py @@ -2958,6 +2958,9 @@ _DIGITS_PAST_THE_INT_LIMIT: Final = "1" * (sys.get_int_max_str_digits() + 1) (('{"n": 1e5}',), '{"n": 1e5}'), (('{"values": false}',), "false"), (('{"values": null}',), '{"values": null}'), + (("[1, 2]",), "[1, 2]"), + (('"x"',), '"x"'), + (("5",), "5"), pytest.param( ('{"values": ' + "[" * 300 + "]" * 300 + "}",), "[" * 300 + "]" * 300, diff --git a/tests/unit/llms/anthropic/chat/test_anthropic_chat_transformation.py b/tests/unit/llms/anthropic/chat/test_anthropic_chat_transformation.py index d4903a91ea4..44471093f49 100644 --- a/tests/unit/llms/anthropic/chat/test_anthropic_chat_transformation.py +++ b/tests/unit/llms/anthropic/chat/test_anthropic_chat_transformation.py @@ -7909,6 +7909,24 @@ def test_convert_tool_response_to_message_keeps_arguments_too_deep_to_decode(arg assert message.content == arguments +@pytest.mark.parametrize("arguments", ["[1, 2]", '"x"', "5"]) +def test_convert_tool_response_to_message_keeps_a_non_object_answer_as_written(arguments: str) -> None: + message: Final = AnthropicConfig.convert_tool_response_to_message( + tool_calls=[ + ChatCompletionToolCallChunk( + id="toolu_1", + type="function", + function=ChatCompletionToolCallFunctionChunk(name=RESPONSE_FORMAT_TOOL_NAME, arguments=arguments), + index=0, + ) + ], + response_format_schema=None, + ) + + assert message is not None + assert message.content == arguments + + _DEEPLY_NESTED: Final = "[" * 300 + "]" * 300 @@ -7926,7 +7944,7 @@ _DEEPLY_NESTED: Final = "[" * 300 + "]" * 300 ) def test_convert_tool_response_to_message_unwraps_a_deeply_nested_answer( arguments: str, schema: dict[str, object] | None, expected: str -): +) -> None: message = AnthropicConfig.convert_tool_response_to_message( tool_calls=[ ChatCompletionToolCallChunk(