[Misc] Use VLLMValidationError consistently in chat completion and completion protocol validators (#36254)
Signed-off-by: umut-polat <52835619+umut-polat@users.noreply.github.com>
This commit is contained in:
@@ -116,3 +116,68 @@ def test_no_reasoning_fields_unchanged():
|
||||
assistant_msg = request.messages[1]
|
||||
assert assistant_msg.get("reasoning") is None
|
||||
assert "reasoning_content" not in assistant_msg
|
||||
|
||||
|
||||
SAMPLE_TOOL = {
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "get_weather",
|
||||
"description": "Get the weather",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {"location": {"type": "string"}},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def test_structured_outputs_with_named_tool_choice_rejected():
|
||||
"""structured_outputs cannot be combined with a named tool_choice."""
|
||||
with pytest.raises(
|
||||
ValueError,
|
||||
match="structured outputs or tools, not both",
|
||||
):
|
||||
ChatCompletionRequest.model_validate(
|
||||
{
|
||||
"messages": [{"role": "user", "content": "Hello"}],
|
||||
"model": "facebook/opt-125m",
|
||||
"tools": [SAMPLE_TOOL],
|
||||
"tool_choice": {
|
||||
"type": "function",
|
||||
"function": {"name": "get_weather"},
|
||||
},
|
||||
"structured_outputs": {"json": {"type": "object"}},
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def test_structured_outputs_with_auto_tool_choice_allowed():
|
||||
"""structured_outputs with tool_choice 'auto' should be allowed."""
|
||||
request = ChatCompletionRequest.model_validate(
|
||||
{
|
||||
"messages": [{"role": "user", "content": "Hello"}],
|
||||
"model": "facebook/opt-125m",
|
||||
"tools": [SAMPLE_TOOL],
|
||||
"tool_choice": "auto",
|
||||
"structured_outputs": {"json": {"type": "object"}},
|
||||
}
|
||||
)
|
||||
assert request.tool_choice == "auto"
|
||||
|
||||
|
||||
def test_multiple_structured_outputs_rejected():
|
||||
"""Only one kind of structured output constraint is allowed."""
|
||||
with pytest.raises(
|
||||
ValueError,
|
||||
match="You can only use one kind of constraints",
|
||||
):
|
||||
ChatCompletionRequest.model_validate(
|
||||
{
|
||||
"messages": [{"role": "user", "content": "Hello"}],
|
||||
"model": "facebook/opt-125m",
|
||||
"structured_outputs": {
|
||||
"json": {"type": "object"},
|
||||
"regex": ".*",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
@@ -740,19 +740,19 @@ class ChatCompletionRequest(OpenAIBaseModel):
|
||||
)
|
||||
# you can only use one kind of constraints for structured outputs
|
||||
if count > 1:
|
||||
raise ValueError(
|
||||
raise VLLMValidationError(
|
||||
"You can only use one kind of constraints for structured "
|
||||
"outputs ('json', 'regex' or 'choice')."
|
||||
"outputs ('json', 'regex' or 'choice').",
|
||||
)
|
||||
# you can only either use structured outputs or tools, not both
|
||||
if count > 1 and data.get("tool_choice", "none") not in (
|
||||
if count > 0 and data.get("tool_choice", "none") not in (
|
||||
"none",
|
||||
"auto",
|
||||
"required",
|
||||
):
|
||||
raise ValueError(
|
||||
raise VLLMValidationError(
|
||||
"You can only either use constraints for structured outputs "
|
||||
"or tools, not both."
|
||||
"or tools, not both.",
|
||||
)
|
||||
return data
|
||||
|
||||
@@ -784,17 +784,21 @@ class ChatCompletionRequest(OpenAIBaseModel):
|
||||
if "tool_choice" in data and data["tool_choice"] is not None:
|
||||
# ensure that if "tool choice" is specified, tools are present
|
||||
if "tools" not in data or data["tools"] is None:
|
||||
raise ValueError("When using `tool_choice`, `tools` must be set.")
|
||||
raise VLLMValidationError(
|
||||
"When using `tool_choice`, `tools` must be set.",
|
||||
parameter="tool_choice",
|
||||
)
|
||||
|
||||
# make sure that tool choice is either a named tool
|
||||
# OR that it's set to "auto" or "required"
|
||||
if data["tool_choice"] not in ["auto", "required"] and not isinstance(
|
||||
data["tool_choice"], dict
|
||||
):
|
||||
raise ValueError(
|
||||
raise VLLMValidationError(
|
||||
f"Invalid value for `tool_choice`: {data['tool_choice']}! "
|
||||
'Only named tools, "none", "auto" or "required" '
|
||||
"are supported."
|
||||
"are supported.",
|
||||
parameter="tool_choice",
|
||||
)
|
||||
|
||||
# ensure that if "tool_choice" is specified as an object,
|
||||
@@ -807,29 +811,33 @@ class ChatCompletionRequest(OpenAIBaseModel):
|
||||
valid_tool = False
|
||||
function = data["tool_choice"].get("function")
|
||||
if not isinstance(function, dict):
|
||||
raise ValueError(
|
||||
raise VLLMValidationError(
|
||||
f"Invalid value for `function`: `{function}` in "
|
||||
f"`tool_choice`! {correct_usage_message}"
|
||||
f"`tool_choice`! {correct_usage_message}",
|
||||
parameter="tool_choice.function",
|
||||
)
|
||||
if "name" not in function:
|
||||
raise ValueError(
|
||||
raise VLLMValidationError(
|
||||
f"Expected field `name` in `function` in "
|
||||
f"`tool_choice`! {correct_usage_message}"
|
||||
f"`tool_choice`! {correct_usage_message}",
|
||||
parameter="tool_choice.function.name",
|
||||
)
|
||||
function_name = function["name"]
|
||||
if not isinstance(function_name, str) or len(function_name) == 0:
|
||||
raise ValueError(
|
||||
raise VLLMValidationError(
|
||||
f"Invalid `name` in `function`: `{function_name}`"
|
||||
f" in `tool_choice`! {correct_usage_message}"
|
||||
f" in `tool_choice`! {correct_usage_message}",
|
||||
parameter="tool_choice.function.name",
|
||||
)
|
||||
for tool in data["tools"]:
|
||||
if tool["function"]["name"] == function_name:
|
||||
valid_tool = True
|
||||
break
|
||||
if not valid_tool:
|
||||
raise ValueError(
|
||||
raise VLLMValidationError(
|
||||
"The tool specified in `tool_choice` does not match any"
|
||||
" of the specified `tools`"
|
||||
" of the specified `tools`",
|
||||
parameter="tool_choice",
|
||||
)
|
||||
return data
|
||||
|
||||
@@ -837,9 +845,9 @@ class ChatCompletionRequest(OpenAIBaseModel):
|
||||
@classmethod
|
||||
def check_generation_prompt(cls, data):
|
||||
if data.get("continue_final_message") and data.get("add_generation_prompt"):
|
||||
raise ValueError(
|
||||
raise VLLMValidationError(
|
||||
"Cannot set both `continue_final_message` and "
|
||||
"`add_generation_prompt` to True."
|
||||
"`add_generation_prompt` to True.",
|
||||
)
|
||||
return data
|
||||
|
||||
@@ -849,8 +857,9 @@ class ChatCompletionRequest(OpenAIBaseModel):
|
||||
if data.get("cache_salt") is not None and (
|
||||
not isinstance(data["cache_salt"], str) or not data["cache_salt"]
|
||||
):
|
||||
raise ValueError(
|
||||
"Parameter 'cache_salt' must be a non-empty string if provided."
|
||||
raise VLLMValidationError(
|
||||
"Parameter 'cache_salt' must be a non-empty string if provided.",
|
||||
parameter="cache_salt",
|
||||
)
|
||||
return data
|
||||
|
||||
|
||||
@@ -447,8 +447,9 @@ class CompletionRequest(OpenAIBaseModel):
|
||||
)
|
||||
|
||||
if prompt_is_empty and embeds_is_empty:
|
||||
raise ValueError(
|
||||
"Either prompt or prompt_embeds must be provided and non-empty."
|
||||
raise VLLMValidationError(
|
||||
"Either prompt or prompt_embeds must be provided and non-empty.",
|
||||
parameter="prompt",
|
||||
)
|
||||
|
||||
return data
|
||||
@@ -459,8 +460,9 @@ class CompletionRequest(OpenAIBaseModel):
|
||||
if data.get("cache_salt") is not None and (
|
||||
not isinstance(data["cache_salt"], str) or not data["cache_salt"]
|
||||
):
|
||||
raise ValueError(
|
||||
"Parameter 'cache_salt' must be a non-empty string if provided."
|
||||
raise VLLMValidationError(
|
||||
"Parameter 'cache_salt' must be a non-empty string if provided.",
|
||||
parameter="cache_salt",
|
||||
)
|
||||
return data
|
||||
|
||||
|
||||
Reference in New Issue
Block a user