fix: token_counter crashed on a tool array parameter without items

_format_type indexed props['items'] unguarded, so token_counter raised
KeyError('items') for any tool whose array-typed parameter omitted items, and
AttributeError when items was a list (JSON-Schema tuple validation). This is
token counting, not request validation, and the module degrades gracefully
elsewhere, so a missing or non-dict items should fall back to the generic any
type instead of raising. It is reachable through the public litellm.token_counter
API for both the OpenAI and Anthropic tool shapes.
This commit is contained in:
otiscuilei 2026-07-08 21:19:16 +08:00
parent cd6e8cdf23
commit 418c2b1708
2 changed files with 53 additions and 2 deletions

View file

@ -814,8 +814,9 @@ def _format_type(props, indent):
return " | ".join([f'"{item}"' for item in props["enum"]])
return "string"
elif type == "array":
# items is required, OpenAI throws an error if it's missing
return f"{_format_type(props['items'], indent)}[]"
items = props.get("items")
items = items if isinstance(items, dict) else {}
return f"{_format_type(items, indent)}[]"
elif type == "object":
return f"{{\n{_format_object_parameters(props, indent + 2)}\n}}"
elif type in ["integer", "number"]:

View file

@ -1114,3 +1114,53 @@ def test_count_content_list_rejects_unknown_type():
message = str(exc_info.value)
assert "Invalid content item type: totally_unknown_block" in message
assert "tool_reference" in message
@pytest.mark.parametrize(
"tools",
[
[
{
"type": "function",
"function": {
"name": "f",
"parameters": {
"type": "object",
"properties": {"tags": {"type": "array"}},
"required": ["tags"],
},
},
}
],
[
{
"type": "function",
"function": {
"name": "f",
"parameters": {
"type": "object",
"properties": {"tags": {"type": "array", "items": [{"type": "string"}]}},
"required": ["tags"],
},
},
}
],
[
{
"name": "f",
"input_schema": {
"type": "object",
"properties": {"tags": {"type": "array"}},
"required": ["tags"],
},
}
],
],
ids=["openai_array_without_items", "openai_array_items_as_list", "anthropic_array_without_items"],
)
def test_token_counter_tool_array_param_missing_or_invalid_items(tools):
result = litellm.token_counter(
model="gpt-4o", messages=[{"role": "user", "content": "hi"}], tools=tools
)
assert isinstance(result, int)
assert result > 0