From acfb83ab76338f9e86a6963a5ae95768e0e0e5a7 Mon Sep 17 00:00:00 2001 From: yasumorishima Date: Sat, 5 Sep 2026 08:35:18 +0900 Subject: [PATCH] test(litellm): cover the tool call that carries no arguments `_parse_tool_call_arguments` returns `{}` for a falsy `arguments` (src/google/adk/models/lite_llm.py:203-204), so a tool call that carries no arguments is dispatched with empty args and is *not* reported as malformed. Nothing covered that: test_litellm.py has no case with empty arguments outside a streaming fixture that accumulates a JSON payload split across chunks, and 6e596635 covers the JSONDecodeError path and its streaming equivalent. The distinction matters because the output alone cannot tell the two apart. With the guard removed, `""` falls through to `except json.JSONDecodeError` and still yields `{}` -- the only difference is the warning. So the test asserts the empty args *and* that nothing was logged as malformed; removing the guard makes it fail. The malformed-JSON handling this PR originally proposed landed in 6e596635 (Close #5896) with better semantics than mine -- dispatch with empty arguments so the tool can return a structured error and the model can retry, rather than dropping the call -- so the source change is dropped in favour of upstream's. --- tests/unittests/models/test_litellm.py | 37 ++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/tests/unittests/models/test_litellm.py b/tests/unittests/models/test_litellm.py index 7abbbaad44f..9be5ad24d80 100644 --- a/tests/unittests/models/test_litellm.py +++ b/tests/unittests/models/test_litellm.py @@ -2985,6 +2985,43 @@ def test_message_to_generate_content_response_tool_call_malformed_arguments_logs assert '{"city":"unterminated' in caplog.text +def test_message_to_generate_content_response_tool_call_without_arguments( + caplog, +): + """A tool call carrying no arguments is dispatched, and is not malformed.""" + message = ChatCompletionAssistantMessage( + role="assistant", + content=None, + tool_calls=[ + ChatCompletionMessageToolCall( + type="function", + id="test_tool_call_id", + function=Function( + name="test_function", + arguments="", + ), + ) + ], + ) + + with caplog.at_level( + logging.WARNING, logger="google_adk.google.adk.models.lite_llm" + ): + response = _message_to_generate_content_response(message) + + function_calls = [ + part.function_call + for part in response.content.parts + if part.function_call is not None + ] + assert len(function_calls) == 1 + assert function_calls[0].name == "test_function" + assert function_calls[0].id == "test_tool_call_id" + assert isinstance(function_calls[0].args, dict) + assert not function_calls[0].args + assert "Malformed" not in caplog.text + + def test_message_to_generate_content_response_inline_tool_call_text(): message = ChatCompletionAssistantMessage( role="assistant",