diff --git a/litellm/litellm_core_utils/prompt_templates/tool_call_json_repair.py b/litellm/litellm_core_utils/prompt_templates/tool_call_json_repair.py index 317ffa7b3b1..1fe57f38ee3 100644 --- a/litellm/litellm_core_utils/prompt_templates/tool_call_json_repair.py +++ b/litellm/litellm_core_utils/prompt_templates/tool_call_json_repair.py @@ -104,12 +104,10 @@ def parse_tool_call_arguments( repaired: Final = _attempt_json_repair(arguments) if repaired is not None: verbose_logger.warning( - "Repaired truncated tool call arguments for tool '%s' (%s). Original (%d chars): %.200s%s", + "Repaired truncated tool call arguments for tool '%s' (%s). Original length: %d chars", tool_name or "", context or "unknown context", len(arguments), - arguments, - "..." if len(arguments) > 200 else "", ) return repaired diff --git a/litellm/llms/ollama/chat/transformation.py b/litellm/llms/ollama/chat/transformation.py index a575a7bb42e..ee795d2f49c 100644 --- a/litellm/llms/ollama/chat/transformation.py +++ b/litellm/llms/ollama/chat/transformation.py @@ -55,11 +55,12 @@ def _to_ollama_tool_call_function(model: str, tool_call: ChatCompletionAssistant name: Final = function.get("name") or "" raw_arguments: Final[object] = function.get("arguments") try: - arguments: Final = _TOOL_CALL_ARGUMENTS.validate_python( + parsed: Final = ( parse_tool_call_arguments(raw_arguments, tool_name=name, context="Ollama chat request") if isinstance(raw_arguments, str) or raw_arguments is None else raw_arguments ) + arguments: Final = _TOOL_CALL_ARGUMENTS.validate_python({} if parsed is None else parsed) except ValueError as e: raise litellm.BadRequestError( message=f"OllamaException - arguments of assistant tool call {tool_call.get('id')!r} are not a JSON object", diff --git a/tests/unit/litellm_core_utils/prompt_templates/test_tool_call_json_repair.py b/tests/unit/litellm_core_utils/prompt_templates/test_tool_call_json_repair.py index 75f4e686137..a23dcfbb882 100644 --- a/tests/unit/litellm_core_utils/prompt_templates/test_tool_call_json_repair.py +++ b/tests/unit/litellm_core_utils/prompt_templates/test_tool_call_json_repair.py @@ -1,4 +1,6 @@ import json +import logging +from typing import Final import pytest @@ -115,3 +117,14 @@ def _distinct_json_objects(count: int) -> str: ) def test_salvage_concatenated_tool_arguments(raw: str, expected: tuple[dict[str, object], ...]) -> None: assert salvage_concatenated_tool_arguments(raw) == expected + + +def test_parse_tool_call_arguments_repair_warning_leaves_out_argument_content( + caplog: pytest.LogCaptureFixture, +) -> None: + with caplog.at_level(logging.WARNING, logger="LiteLLM"): + result: Final = parse_tool_call_arguments('{"patient": "Jane Doe"', tool_name="lookup", context="test") + + assert result == {"patient": "Jane Doe"} + assert "Repaired truncated tool call arguments for tool 'lookup'" in caplog.text + assert "Jane Doe" not in caplog.text diff --git a/tests/unit/llms/ollama/test_ollama_chat_transformation.py b/tests/unit/llms/ollama/test_ollama_chat_transformation.py index cabfdcd117c..acd52987144 100644 --- a/tests/unit/llms/ollama/test_ollama_chat_transformation.py +++ b/tests/unit/llms/ollama/test_ollama_chat_transformation.py @@ -1077,10 +1077,12 @@ class TestOllamaToolCallTransformation: [ ("", {}), (None, {}), + ("null", {}), ('{"path": "a.rs"', {"path": "a.rs"}), + ('{"path": "a.rs", "limit": 2}', {"path": "a.rs", "limit": 2}), ({"path": "a.rs"}, {"path": "a.rs"}), ], - ids=["empty", "null", "truncated", "object"], + ids=["empty", "null", "null_string", "truncated", "non_string_value", "object"], ) def test_replayed_tool_call_arguments_are_forwarded_as_an_object(self, arguments: object, expected: object): result: Final = OllamaChatConfig().transform_request( @@ -1097,8 +1099,8 @@ class TestOllamaToolCallTransformation: @pytest.mark.parametrize( "arguments", - ['{"path": "a.rs"}{"path": "b.rs"}', '["a.rs", "b.rs"]'], - ids=["glued", "array"], + ['{"path": "a.rs"}{"path": "b.rs"}', '["a.rs", "b.rs"]', '[["path", "a.rs"]]'], + ids=["glued", "array", "pairs"], ) def test_replayed_tool_call_arguments_that_are_not_an_object_answer_400(self, arguments: str): with pytest.raises(litellm.BadRequestError) as exc_info: @@ -1111,6 +1113,7 @@ class TestOllamaToolCallTransformation: assert exc_info.value.status_code == 400 assert "call_abc123" in str(exc_info.value) + class TestOllamaStreamingParallelToolCallIndex: """Regression for https://github.com/BerriAI/litellm/issues/33678.