fix(ollama): raise BadRequestError on malformed tool call JSON arguments

Wrap json.loads() in transform_request so callers get a descriptive
litellm.BadRequestError instead of a raw json.JSONDecodeError when an
Ollama-compatible model returns truncated or otherwise invalid JSON in
tool call arguments.

Fixes #25985
This commit is contained in:
octo-patch 2026-04-18 09:11:35 +08:00
parent 850fe595ac
commit 9af3e87de7
2 changed files with 91 additions and 2 deletions

View file

@ -1,6 +1,7 @@
import json
import time
from litellm._uuid import uuid
from litellm._logging import verbose_logger
from typing import (
TYPE_CHECKING,
Any,
@ -272,7 +273,18 @@ class OllamaChatConfig(BaseConfig):
if typed_tool["type"] == "function":
arguments = {}
if "arguments" in typed_tool["function"]:
arguments = json.loads(typed_tool["function"]["arguments"])
raw_args = typed_tool["function"]["arguments"]
try:
arguments = json.loads(raw_args)
except json.JSONDecodeError as e:
verbose_logger.error(
f"Failed to parse tool call arguments as JSON: {raw_args!r}. Error: {e}"
)
raise litellm.BadRequestError(
message=f"Tool call arguments contain malformed JSON: {e.msg}. Raw arguments: {raw_args!r}",
model="ollama",
llm_provider="ollama",
)
ollama_tool_call = OllamaToolCall(
function=OllamaToolCallFunction(
name=typed_tool["function"].get("name") or "",

View file

@ -338,7 +338,84 @@ class TestOllamaToolCalling:
Issue: https://github.com/BerriAI/litellm/issues/18922
"""
def test_tools_passed_directly_without_capability_check(self):
def test_transform_request_malformed_tool_call_arguments_raises_bad_request(self):
"""Test that malformed JSON in tool call arguments raises BadRequestError.
Regression: json.JSONDecodeError was previously raised directly, wrapping it
in litellm.BadRequestError gives callers an actionable error with context.
Issue: https://github.com/BerriAI/litellm/issues/25985
"""
config = OllamaChatConfig()
messages = cast(
list[AllMessageValues],
[
{
"role": "assistant",
"content": None,
"tool_calls": [
{
"id": "call_123",
"type": "function",
"function": {
"name": "get_weather",
"arguments": '{"city": "Toky', # truncated JSON
},
}
],
}
],
)
import litellm
with pytest.raises(litellm.BadRequestError) as exc_info:
config.transform_request(
model="qwen3:14b",
messages=messages,
optional_params={},
litellm_params={},
headers={},
)
assert "malformed JSON" in str(exc_info.value)
def test_transform_request_valid_tool_call_arguments_passes(self):
"""Test that valid JSON tool call arguments are parsed without raising an exception."""
config = OllamaChatConfig()
messages = cast(
list[AllMessageValues],
[
{
"role": "assistant",
"content": None,
"tool_calls": [
{
"id": "call_123",
"type": "function",
"function": {
"name": "get_weather",
"arguments": '{"city": "Tokyo"}',
},
}
],
}
],
)
# Should not raise any exception
result = config.transform_request(
model="qwen3:14b",
messages=messages,
optional_params={},
litellm_params={},
headers={},
)
assert "messages" in result
"""Test that tools are passed directly to Ollama without model capability checks.
Previously, the code called litellm.get_model_info() which could fail