Unverified Commit 18013df6 authored by jigangz's avatar jigangz Committed by GitHub
Browse files

[Bugfix] Reject empty tools array with HTTP 400 (#39780)


Signed-off-by: default avatarJigang Zhou <zjg0907008@gmail.com>
Co-authored-by: default avatarClaude <noreply@anthropic.com>
parent c0722f22
...@@ -328,7 +328,7 @@ def test_extract_tool_calls_streaming_incremental( ...@@ -328,7 +328,7 @@ def test_extract_tool_calls_streaming_incremental(
expected_content, expected_content,
): ):
"""Verify the Ernie45 Parser streaming behavior by verifying each chunk is as expected.""" # noqa: E501 """Verify the Ernie45 Parser streaming behavior by verifying each chunk is as expected.""" # noqa: E501
request = ChatCompletionRequest(model=MODEL, messages=[], tools=[]) request = ChatCompletionRequest(model=MODEL, messages=[])
tool_calls_dict = {} tool_calls_dict = {}
for delta_message in stream_delta_message_generator( for delta_message in stream_delta_message_generator(
......
...@@ -484,7 +484,7 @@ def test_extract_tool_calls_streaming_incremental( ...@@ -484,7 +484,7 @@ def test_extract_tool_calls_streaming_incremental(
expected_content, expected_content,
): ):
"""Verify the XLAM Parser streaming behavior by verifying each chunk is as expected.""" # noqa: E501 """Verify the XLAM Parser streaming behavior by verifying each chunk is as expected.""" # noqa: E501
request = ChatCompletionRequest(model=MODEL, messages=[], tools=[]) request = ChatCompletionRequest(model=MODEL, messages=[])
chunks = [] chunks = []
for delta_message in stream_delta_message_generator( for delta_message in stream_delta_message_generator(
......
...@@ -26,15 +26,15 @@ def test_chat_completion_request_with_no_tools(): ...@@ -26,15 +26,15 @@ def test_chat_completion_request_with_no_tools():
) )
assert request.tool_choice == "none" assert request.tool_choice == "none"
# tools key present but empty # tools key present but empty -- should be rejected
request = ChatCompletionRequest.model_validate( with pytest.raises(ValueError, match="must not be an empty array"):
{ ChatCompletionRequest.model_validate(
"messages": [{"role": "user", "content": "Hello"}], {
"model": "facebook/opt-125m", "messages": [{"role": "user", "content": "Hello"}],
"tools": [], "model": "facebook/opt-125m",
} "tools": [],
) }
assert request.tool_choice == "none" )
@pytest.mark.parametrize("tool_choice", ["auto", "required"]) @pytest.mark.parametrize("tool_choice", ["auto", "required"])
......
...@@ -681,6 +681,18 @@ class ChatCompletionRequest(OpenAIBaseModel): ...@@ -681,6 +681,18 @@ class ChatCompletionRequest(OpenAIBaseModel):
@model_validator(mode="before") @model_validator(mode="before")
@classmethod @classmethod
def check_tool_usage(cls, data): def check_tool_usage(cls, data):
if isinstance(data, ValueError):
raise data
if not isinstance(data, dict):
return data
# Reject empty tools array, matching OpenAI API behavior
if data.get("tools") == []:
raise ValueError(
"`tools` must not be an empty array. "
"Either provide at least one tool or omit the field entirely."
)
# if "tool_choice" is not specified but tools are provided, # if "tool_choice" is not specified but tools are provided,
# default to "auto" tool_choice # default to "auto" tool_choice
if "tool_choice" not in data and data.get("tools"): if "tool_choice" not in data and data.get("tools"):
...@@ -707,18 +719,6 @@ class ChatCompletionRequest(OpenAIBaseModel): ...@@ -707,18 +719,6 @@ class ChatCompletionRequest(OpenAIBaseModel):
"are supported." "are supported."
) )
# if tool_choice is "required" but the "tools" list is empty,
# override the data to behave like "none" to align with
# OpenAI’s behavior.
if (
data["tool_choice"] == "required"
and isinstance(data["tools"], list)
and len(data["tools"]) == 0
):
data["tool_choice"] = "none"
del data["tools"]
return data
# ensure that if "tool_choice" is specified as an object, # ensure that if "tool_choice" is specified as an object,
# it matches a valid tool # it matches a valid tool
correct_usage_message = ( correct_usage_message = (
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment