@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / 346cf163a1
Fork 0

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")