@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / a307ac0734
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 10, 06:30 UTC

[responsesAPI] add unit test for optional function tool call id (#32036)

Signed-off-by: Andrew Xia <axia@fb.com> Co-authored-by: Andrew Xia <axia@fb.com>

Andrew Xia committed Jan 12, 2026 at 19:14 UTC a307ac073432d3f224c58a54d363fa93f6a659f4
1 file changed +118
tests/entrypoints/test_responses_utils.py
+118
@@ -2,6 +2,7 @@
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 import pytest
5 +from openai.types.chat import ChatCompletionMessageParam
6 from openai.types.responses.response_function_tool_call import ResponseFunctionToolCall
7 from openai.types.responses.response_function_tool_call_output_item import (
8 ResponseFunctionToolCallOutputItem,
@@ -14,8 +15,10 @@ from openai.types.responses.response_reasoning_item import (
15 Summary,
16 )
17
18 +from vllm.entrypoints.constants import MCP_PREFIX
19 from vllm.entrypoints.responses_utils import (
20 _construct_single_message_from_response_item,
21 + _maybe_combine_reasoning_and_tool_call,
22 construct_chat_messages_with_tool_call,
23 convert_tool_responses_to_completions_format,
24 )
@@ -160,3 +163,118 @@ class TestResponsesUtils:
163 formatted_item = _construct_single_message_from_response_item(output_item)
164 assert formatted_item["role"] == "assistant"
165 assert formatted_item["content"] == "dongyi"
166 +
167 +
168 +class TestMaybeCombineReasoningAndToolCall:
169 + """Tests for _maybe_combine_reasoning_and_tool_call function."""
170 +
171 + def test_returns_none_when_item_id_is_none(self):
172 + """
173 + Test fix from PR #31999: when item.id is None, should return None
174 + instead of raising TypeError on startswith().
175 + """
176 + item = ResponseFunctionToolCall(
177 + type="function_call",
178 + id=None, # This was causing TypeError before the fix
179 + call_id="call_123",
180 + name="test_function",
181 + arguments="{}",
182 + )
183 + messages: list[ChatCompletionMessageParam] = []
184 +
185 + result = _maybe_combine_reasoning_and_tool_call(item, messages)
186 +
187 + assert result is None
188 +
189 + def test_returns_none_when_id_does_not_start_with_mcp_prefix(self):
190 + """Test that non-MCP tool calls are not combined."""
191 + item = ResponseFunctionToolCall(
192 + type="function_call",
193 + id="regular_id", # Does not start with MCP_PREFIX
194 + call_id="call_123",
195 + name="test_function",
196 + arguments="{}",
197 + )
198 + messages = [{"role": "assistant", "reasoning": "some reasoning"}]
199 +
200 + result = _maybe_combine_reasoning_and_tool_call(item, messages)
201 +
202 + assert result is None
203 +
204 + def test_returns_none_when_last_message_is_not_assistant(self):
205 + """Test that non-assistant last message returns None."""
206 + item = ResponseFunctionToolCall(
207 + type="function_call",
208 + id=f"{MCP_PREFIX}tool_id",
209 + call_id="call_123",
210 + name="test_function",
211 + arguments="{}",
212 + )
213 + messages = [{"role": "user", "content": "hello"}]
214 +
215 + result = _maybe_combine_reasoning_and_tool_call(item, messages)
216 +
217 + assert result is None
218 +
219 + def test_returns_none_when_last_message_has_no_reasoning(self):
220 + """Test that assistant message without reasoning returns None."""
221 + item = ResponseFunctionToolCall(
222 + type="function_call",
223 + id=f"{MCP_PREFIX}tool_id",
224 + call_id="call_123",
225 + name="test_function",
226 + arguments="{}",
227 + )
228 + messages = [{"role": "assistant", "content": "some content"}]
229 +
230 + result = _maybe_combine_reasoning_and_tool_call(item, messages)
231 +
232 + assert result is None
233 +
234 + def test_combines_reasoning_and_mcp_tool_call(self):
235 + """Test successful combination of reasoning message and MCP tool call."""
236 + item = ResponseFunctionToolCall(
237 + type="function_call",
238 + id=f"{MCP_PREFIX}tool_id",
239 + call_id="call_123",
240 + name="test_function",
241 + arguments='{"arg": "value"}',
242 + )
243 + messages = [{"role": "assistant", "reasoning": "I need to call this tool"}]
244 +
245 + result = _maybe_combine_reasoning_and_tool_call(item, messages)
246 +
247 + assert result is not None
248 + assert result["role"] == "assistant"
249 + assert result["reasoning"] == "I need to call this tool"
250 + assert "tool_calls" in result
251 + assert len(result["tool_calls"]) == 1
252 + assert result["tool_calls"][0]["id"] == "call_123"
253 + assert result["tool_calls"][0]["function"]["name"] == "test_function"
254 + assert result["tool_calls"][0]["function"]["arguments"] == '{"arg": "value"}'
255 + assert result["tool_calls"][0]["type"] == "function"
256 +
257 + def test_returns_none_for_non_function_tool_call_type(self):
258 + """Test that non-ResponseFunctionToolCall items return None."""
259 + # Pass a dict instead of ResponseFunctionToolCall
260 + item = {"type": "message", "content": "hello"}
261 + messages = [{"role": "assistant", "reasoning": "some reasoning"}]
262 +
263 + result = _maybe_combine_reasoning_and_tool_call(item, messages)
264 +
265 + assert result is None
266 +
267 + def test_returns_none_when_id_is_empty_string(self):
268 + """Test that empty string id returns None (falsy check)."""
269 + item = ResponseFunctionToolCall(
270 + type="function_call",
271 + id="", # Empty string is falsy
272 + call_id="call_123",
273 + name="test_function",
274 + arguments="{}",
275 + )
276 + messages = [{"role": "assistant", "reasoning": "some reasoning"}]
277 +
278 + result = _maybe_combine_reasoning_and_tool_call(item, messages)
279 +
280 + assert result is None