A high-throughput and memory-efficient inference and serving engine for LLMs
Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git
Synced Nov 3, 04:15 UTC
[Frontend] Normalize reasoning_content to reasoning for client compatibility (#42664)
Signed-off-by: Ben Browning <bbrownin@redhat.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Ben Browning committed
May 21, 2026 at 00:23 UTC
346cf163a11b55e069aa3143ae2878967393ddc2
2 files changed
+66
-7
tests/tool_use/test_chat_completion_request_validations.py
+55
@@ -61,3 +61,58 @@ def test_chat_completion_request_with_tool_choice_but_no_tools(tool_choice):
61
"tools": None,
62
}
63
)
64
+
65
+
66
+def test_reasoning_content_normalized_to_reasoning():
67
+ request = ChatCompletionRequest.model_validate(
68
+ {
69
+ "messages": [
70
+ {"role": "user", "content": "What is 2+2?"},
71
+ {
72
+ "role": "assistant",
73
+ "content": "4",
74
+ "reasoning_content": "2+2 equals 4",
75
+ },
76
+ {"role": "user", "content": "Are you sure?"},
77
+ ],
78
+ "model": "facebook/opt-125m",
79
+ }
80
+ )
81
+ assistant_msg = request.messages[1]
82
+ assert assistant_msg.get("reasoning") == "2+2 equals 4"
83
+ assert "reasoning_content" not in assistant_msg
84
+
85
+
86
+def test_reasoning_takes_precedence_over_reasoning_content():
87
+ request = ChatCompletionRequest.model_validate(
88
+ {
89
+ "messages": [
90
+ {"role": "user", "content": "What is 2+2?"},
91
+ {
92
+ "role": "assistant",
93
+ "content": "4",
94
+ "reasoning": "from reasoning field",
95
+ "reasoning_content": "from reasoning_content field",
96
+ },
97
+ ],
98
+ "model": "facebook/opt-125m",
99
+ }
100
+ )
101
+ assistant_msg = request.messages[1]
102
+ assert assistant_msg.get("reasoning") == "from reasoning field"
103
+ assert "reasoning_content" not in assistant_msg
104
+
105
+
106
+def test_no_reasoning_fields_unchanged():
107
+ request = ChatCompletionRequest.model_validate(
108
+ {
109
+ "messages": [
110
+ {"role": "user", "content": "Hello"},
111
+ {"role": "assistant", "content": "Hi"},
112
+ ],
113
+ "model": "facebook/opt-125m",
114
+ }
115
+ )
116
+ assistant_msg = request.messages[1]
117
+ assert assistant_msg.get("reasoning") is None
118
+ assert "reasoning_content" not in assistant_msg
vllm/entrypoints/openai/chat_completion/protocol.py
+11
-7
@@ -413,13 +413,14 @@ class ChatCompletionRequest(OpenAIBaseModel):
413
414
@model_validator(mode="before")
415
@classmethod
416
- def _materialize_tool_calls_before(cls, data: Any) -> Any:
417
- """Eagerly convert tool_calls generators/iterators to lists.
418
-
419
- Must run before Pydantic field validation so that one-shot
420
- generators are not consumed during union type matching of
421
- ChatCompletionAssistantMessageParam (which types tool_calls
422
- as Iterable[...]).
416
+ def _normalize_messages_before(cls, data: Any) -> Any:
417
+ """Pre-process message dicts before Pydantic field validation.
418
+
419
+ Performs two normalizations in a single pass:
420
+ - Converts tool_calls generators/iterators to lists so one-shot
421
+ generators are not consumed during union type matching.
422
+ - Renames the deprecated ``reasoning_content`` field to
423
+ ``reasoning`` so downstream code only needs to check one field.
424
"""
425
if not isinstance(data, dict):
426
return data
@@ -432,6 +433,9 @@ class ChatCompletionRequest(OpenAIBaseModel):
433
tool_calls = msg.get("tool_calls")
434
if tool_calls is not None and not isinstance(tool_calls, list):
435
msg["tool_calls"] = list(tool_calls)
436
+ reasoning_content = msg.pop("reasoning_content", None)
437
+ if reasoning_content is not None and msg.get("reasoning") is None:
438
+ msg["reasoning"] = reasoning_content
439
return data
440
441
@model_validator(mode="after")