Skip to content
Closed
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
81 changes: 53 additions & 28 deletions vllm/entrypoints/openai/chat_completion/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -616,19 +616,22 @@ def check_structured_outputs_count(cls, data):
)
# you can only use one kind of constraints for structured outputs
if count > 1:
raise ValueError(
"You can only use one kind of constraints for structured "
"outputs ('json', 'regex' or 'choice')."
raise VLLMValidationError(
"You can only use one kind of constraints "
"for structured outputs "
"('json', 'regex' or 'choice').",
parameter="structured_outputs",
)
# you can only either use structured outputs or tools, not both
if count > 1 and data.get("tool_choice", "none") not in (
"none",
"auto",
"required",
):
raise ValueError(
"You can only either use constraints for structured outputs "
"or tools, not both."
raise VLLMValidationError(
"You can only either use constraints for "
"structured outputs or tools, not both.",
parameter="structured_outputs",
)
return data

Expand All @@ -648,17 +651,24 @@ def check_tool_usage(cls, data):
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",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parameter here should be tools

value=data.get("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(
f"Invalid value for `tool_choice`: {data['tool_choice']}! "
'Only named tools, "none", "auto" or "required" '
"are supported."
raise VLLMValidationError(
"Invalid value for `tool_choice`: "
f"{data['tool_choice']}! "
'Only named tools, "none", "auto" '
'or "required" are supported.',
parameter="tool_choice",
value=data.get("tool_choice"),
)

# if tool_choice is "required" but the "tools" list is empty,
Expand All @@ -683,39 +693,52 @@ def check_tool_usage(cls, data):
valid_tool = False
function = data["tool_choice"].get("function")
if not isinstance(function, dict):
raise ValueError(
f"Invalid value for `function`: `{function}` in "
f"`tool_choice`! {correct_usage_message}"
raise VLLMValidationError(
"Invalid value for `function`: "
f"`{function}` in `tool_choice`! "
f"{correct_usage_message}",
parameter="tool_choice",
value=data.get("tool_choice"),
)
if "name" not in function:
raise ValueError(
f"Expected field `name` in `function` in "
f"`tool_choice`! {correct_usage_message}"
raise VLLMValidationError(
"Expected field `name` in `function`"
f" in `tool_choice`! "
f"{correct_usage_message}",
parameter="tool_choice",
value=data.get("tool_choice"),
)
function_name = function["name"]
if not isinstance(function_name, str) or len(function_name) == 0:
raise ValueError(
f"Invalid `name` in `function`: `{function_name}`"
f" in `tool_choice`! {correct_usage_message}"
raise VLLMValidationError(
"Invalid `name` in `function`: "
f"`{function_name}` in "
f"`tool_choice`! "
f"{correct_usage_message}",
parameter="tool_choice",
value=data.get("tool_choice"),
)
for tool in data["tools"]:
if tool["function"]["name"] == function_name:
valid_tool = True
break
if not valid_tool:
raise ValueError(
"The tool specified in `tool_choice` does not match any"
" of the specified `tools`"
raise VLLMValidationError(
"The tool specified in `tool_choice` "
"does not match any of the "
"specified `tools`.",
parameter="tool_choice",
value=data.get("tool_choice"),
)
return data

@model_validator(mode="before")
@classmethod
def check_generation_prompt(cls, data):
if data.get("continue_final_message") and data.get("add_generation_prompt"):
raise ValueError(
"Cannot set both `continue_final_message` and "
"`add_generation_prompt` to True."
raise VLLMValidationError(
"Cannot set both `continue_final_message` "
"and `add_generation_prompt` to True.",
)
Comment on lines +739 to 742
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

For consistency with the other changes in this PR, it would be better to specify which parameter is causing the validation error. Since this is a conflict between two parameters, you can pick one to report. add_generation_prompt seems like a reasonable choice.

Suggested change
raise VLLMValidationError(
"Cannot set both `continue_final_message` "
"and `add_generation_prompt` to True.",
)
raise VLLMValidationError(
"Cannot set both `continue_final_message` "
"and `add_generation_prompt` to True.",
parameter="add_generation_prompt",
)

return data

Expand All @@ -725,8 +748,10 @@ def check_cache_salt_support(cls, data):
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",
value=data.get("cache_salt"),
)
return data

Expand Down
10 changes: 6 additions & 4 deletions vllm/entrypoints/openai/completion/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -427,8 +427,8 @@ def validate_prompt_and_prompt_embeds(cls, data):
)

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.",
)
Comment on lines +430 to 432
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

For consistency with the goal of this PR to provide structured error context, it's a good idea to include the parameter that failed validation. In this case, since either prompt or prompt_embeds is required, you can specify prompt as the parameter.

Suggested change
raise VLLMValidationError(
"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
Expand All @@ -439,8 +439,10 @@ def check_cache_salt_support(cls, data):
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",
value=data.get("cache_salt"),
)
return data

Expand Down
Loading