Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion src/google/adk/models/lite_llm.py
Original file line number Diff line number Diff line change
Expand Up @@ -2455,9 +2455,24 @@ def _message_to_generate_content_response(
for tool_call in tool_calls:
if tool_call.type == "function":
thought_signature = _extract_thought_signature_from_tool_call(tool_call)
try:
args = _parse_tool_call_arguments(tool_call.function.arguments)
except json.JSONDecodeError as exc:
# Malformed tool-call arguments are treated as a recoverable model
# error rather than a crash: surface empty args so downstream tool
# dispatch reports the mismatch back to the model for a retry.
logger.warning(
"Failed to parse arguments of tool call %r for function %r as"
" JSON (%s). Falling back to empty args so the model can"
" retry.",
tool_call.id,
tool_call.function.name,
exc,
)
args = {}
part = types.Part.from_function_call(
name=tool_call.function.name,
args=_parse_tool_call_arguments(tool_call.function.arguments),
args=args,
)
function_call = part.function_call
if function_call is None:
Expand Down
53 changes: 53 additions & 0 deletions tests/unittests/models/test_litellm.py
Original file line number Diff line number Diff line change
Expand Up @@ -2664,6 +2664,59 @@ def test_message_to_generate_content_response_tool_call_accepts_unquoted_json_ke
}


def test_message_to_generate_content_response_tool_call_with_malformed_json_arguments():
message = ChatCompletionAssistantMessage(
role="assistant",
content=None,
tool_calls=[
ChatCompletionMessageToolCall(
type="function",
id="test_tool_call_id",
function=Function(
name="test_function",
arguments='{"city": "unterminated',
),
)
],
)

response = _message_to_generate_content_response(message)

assert response.content.role == "model"
assert response.content.parts[0].function_call.name == "test_function"
assert response.content.parts[0].function_call.id == "test_tool_call_id"
assert response.content.parts[0].function_call.args == {}


def test_message_to_generate_content_response_tool_call_malformed_json_warns(
caplog,
):
message = ChatCompletionAssistantMessage(
role="assistant",
content=None,
tool_calls=[
ChatCompletionMessageToolCall(
type="function",
id="test_tool_call_id",
function=Function(
name="test_function",
arguments='{"city": "unterminated',
),
)
],
)

with caplog.at_level(logging.WARNING, logger="google_adk"):
response = _message_to_generate_content_response(message)

assert response.content.parts[0].function_call.args == {}
assert any(
record.levelno >= logging.WARNING
for record in caplog.records
if "Failed to parse arguments" in record.getMessage()
)


def test_message_to_generate_content_response_inline_tool_call_text():
message = ChatCompletionAssistantMessage(
role="assistant",
Expand Down