[Frontend] Migrate Responses harmony input validation to VLLMValidationError (#55701)

Signed-off-by: shaolila <shaolila@buaa.edu.cn> Co-authored-by: shaolila <shaolila@buaa.edu.cn>

Adababy committed Sep 7, 2026 at 21:30 UTC 167858ea174efd0655ae821d3f3a50b51d721525
3 files changed +36 -7
tests/entrypoints/openai/responses/test_harmony_utils.py
+11
@@ -16,6 +16,7 @@ from vllm.entrypoints.openai.responses.harmony import (
16 harmony_to_response_output,
17 response_previous_input_to_harmony,
18 )
19 +from vllm.exceptions import VLLMValidationError
20
21
22 class TestResponsePreviousInputToHarmony:
@@ -92,6 +93,16 @@ class TestResponsePreviousInputToHarmony:
93 assert messages[0].author.name == "functions.empty_tool"
94 assert messages[0].content[0].text == ""
95
96 + def test_chat_message_without_role_raises_validation_error(self):
97 + """A chat-format message missing the 'role' key is invalid input."""
98 + chat_msg = {"content": "hello, no role here"}
99 +
100 + with pytest.raises(
101 + VLLMValidationError, match="Message has no 'role' key"
102 + ) as exc_info:
103 + response_previous_input_to_harmony(chat_msg)
104 + assert exc_info.value.parameter == "input"
105 +
106
107 class TestHarmonyToResponseOutput:
108 """Tests for harmony_to_response_output function."""
tests/entrypoints/openai/responses/test_response_input_to_harmony.py
+12 -4
@@ -15,6 +15,7 @@ from openai.types.responses.response_reasoning_item import (
15 from openai_harmony import DeveloperContent, Role
16
17 from vllm.entrypoints.openai.responses.harmony import response_input_to_harmony
18 +from vllm.exceptions import VLLMValidationError
19
20 # ---------------------------------------------------------------------------
21 # Shared fixtures
@@ -252,7 +253,9 @@ class TestResponseInputToHarmonyMessage:
253 assert msg.author.name == "functions.get_weather"
254
255 def test_function_call_output_raises_if_no_matching_call(self):
255 - with pytest.raises(ValueError, match="No call message found for"):
256 + with pytest.raises(
257 + VLLMValidationError, match="No call message found for"
258 + ) as exc_info:
259 response_input_to_harmony(
260 {
261 "type": "function_call_output",
@@ -261,21 +264,26 @@ class TestResponseInputToHarmonyMessage:
264 },
265 prev_responses=[_PREV_CALL],
266 )
267 + assert exc_info.value.parameter == "input"
268
269 def test_function_call_output_raises_on_empty_prev_responses(self):
266 - with pytest.raises(ValueError, match="No call message found for"):
270 + with pytest.raises(
271 + VLLMValidationError, match="No call message found for"
272 + ) as exc_info:
273 response_input_to_harmony(
274 {"type": "function_call_output", "call_id": "call_test", "output": "x"},
275 prev_responses=[],
276 )
277 + assert exc_info.value.parameter == "input"
278
279 # -----------------------------------------------------------------------
280 # Error cases
281 # -----------------------------------------------------------------------
282
276 - def test_unknown_type_raises_value_error(self):
277 - with pytest.raises(ValueError, match="Unknown input type"):
283 + def test_unknown_type_raises_validation_error(self):
284 + with pytest.raises(VLLMValidationError, match="Unknown input type") as exc_info:
285 response_input_to_harmony(
286 {"type": "image_url", "url": "https://example.com/img.png"},
287 prev_responses=[],
288 )
289 + assert exc_info.value.parameter == "input"
vllm/entrypoints/openai/responses/harmony.py
+13 -3
@@ -40,6 +40,7 @@ from vllm.entrypoints.openai.responses.protocol import (
40 ResponseInputOutputItem,
41 ResponsesRequest,
42 )
43 +from vllm.exceptions import VLLMValidationError
44 from vllm.logger import init_logger
45 from vllm.utils import random_uuid
46
@@ -88,7 +89,10 @@ def _parse_chat_format_message(chat_msg: dict) -> list[Message]:
89 """Parse an OpenAI chat-format dict into Harmony messages."""
90 role = chat_msg.get("role")
91 if role is None:
91 - raise ValueError(f"Message has no 'role' key: {chat_msg}")
92 + raise VLLMValidationError(
93 + f"Message has no 'role' key: {chat_msg}",
94 + parameter="input",
95 + )
96
97 # Assistant message with tool calls
98 tool_calls = chat_msg.get("tool_calls")
@@ -184,7 +188,10 @@ def response_input_to_harmony(
188 call_response = prev_response
189 break
190 if call_response is None:
187 - raise ValueError(f"No call message found for {call_id}")
191 + raise VLLMValidationError(
192 + f"No call message found for {call_id}",
193 + parameter="input",
194 + )
195 msg = Message.from_author_and_content(
196 Author.new(Role.TOOL, f"functions.{call_response.name}"),
197 response_msg["output"],
@@ -205,7 +212,10 @@ def response_input_to_harmony(
212 msg = msg.with_recipient(f"functions.{response_msg['name']}")
213 msg = msg.with_content_type("json")
214 else:
208 - raise ValueError(f"Unknown input type: {response_msg['type']}")
215 + raise VLLMValidationError(
216 + f"Unknown input type: {response_msg['type']}",
217 + parameter="input",
218 + )
219 return msg
220
221