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 14, 05:30 UTC
[GPT-OSS] Add support for arrays at tool message content (#25593)
Signed-off-by: Luis Tomas Bolivar <ltomasbo@redhat.com>
Luis Tomas Bolivar committed
Oct 10, 2025 at 11:00 UTC
3ee202ea1e0f8a1b5a4f51d2746af97a1ad10139
2 files changed
+139
tests/entrypoints/openai/test_serving_chat.py
+132
@@ -207,6 +207,138 @@ async def test_gpt_oss_multi_turn_chat(gptoss_client: OpenAI, with_tool_parser:
207
)
208
209
210
+@pytest.mark.asyncio
211
+async def test_gpt_oss_tool_message_array_content(gptoss_client: OpenAI,
212
+ with_tool_parser: bool):
213
+ """Test that tool messages support both string and array content formats."""
214
+ if not with_tool_parser:
215
+ pytest.skip("skip non-tool for array content tests")
216
+
217
+ tools = [{
218
+ "type": "function",
219
+ "function": {
220
+ "name": "get_weather",
221
+ "description": "Get the current weather in a given location",
222
+ "parameters": {
223
+ "type": "object",
224
+ "properties": {
225
+ "city": {
226
+ "type": "string"
227
+ },
228
+ "state": {
229
+ "type": "string"
230
+ },
231
+ },
232
+ "required": ["city", "state"],
233
+ },
234
+ },
235
+ }]
236
+
237
+ # Test 1: Tool message with string content
238
+ messages_string = [{
239
+ "role": "user",
240
+ "content": "What's the weather in Paris?"
241
+ }, {
242
+ "role":
243
+ "assistant",
244
+ "tool_calls": [{
245
+ "id": "call_123",
246
+ "type": "function",
247
+ "function": {
248
+ "name": "get_weather",
249
+ "arguments": '{"city": "Paris", "state": "TX"}'
250
+ }
251
+ }]
252
+ }, {
253
+ "role": "tool",
254
+ "content": "The weather in Paris, TX is sunny, 22°C"
255
+ }]
256
+
257
+ response_string = await gptoss_client.chat.completions.create(
258
+ model=GPT_OSS_MODEL_NAME,
259
+ messages=messages_string,
260
+ tools=tools,
261
+ temperature=0.0,
262
+ )
263
+
264
+ assert response_string is not None
265
+ assert response_string.choices[0].message is not None
266
+
267
+ # Test 2: Tool message with array content
268
+ messages_array = [{
269
+ "role": "user",
270
+ "content": "What's the weather in Dallas?"
271
+ }, {
272
+ "role":
273
+ "assistant",
274
+ "tool_calls": [{
275
+ "id": "call_456",
276
+ "type": "function",
277
+ "function": {
278
+ "name": "get_weather",
279
+ "arguments": '{"city": "Dallas", "state": "TX"}'
280
+ }
281
+ }]
282
+ }, {
283
+ "role":
284
+ "tool",
285
+ "content": [{
286
+ "type": "text",
287
+ "text": "f2e897a7-2705-4337-8193-2a8f57b81618"
288
+ }]
289
+ }]
290
+
291
+ response_array = await gptoss_client.chat.completions.create(
292
+ model=GPT_OSS_MODEL_NAME,
293
+ messages=messages_array,
294
+ tools=tools,
295
+ temperature=0.0,
296
+ )
297
+
298
+ assert response_array is not None
299
+ assert response_array.choices[0].message is not None
300
+
301
+ # Test 3: Tool message with multiple array content items
302
+ messages_multi_array = [{
303
+ "role": "user",
304
+ "content": "Search for information"
305
+ }, {
306
+ "role":
307
+ "assistant",
308
+ "tool_calls": [{
309
+ "id": "call_789",
310
+ "type": "function",
311
+ "function": {
312
+ "name": "get_weather",
313
+ "arguments": '{"city": "Austin", "state": "TX"}'
314
+ }
315
+ }]
316
+ }, {
317
+ "role":
318
+ "tool",
319
+ "content": [{
320
+ "type": "text",
321
+ "text": "Weather data: "
322
+ }, {
323
+ "type": "text",
324
+ "text": "Austin, TX - Partly cloudy, 25°C"
325
+ }, {
326
+ "type": "text",
327
+ "text": " with 60% humidity"
328
+ }]
329
+ }]
330
+
331
+ response_multi_array = await gptoss_client.chat.completions.create(
332
+ model=GPT_OSS_MODEL_NAME,
333
+ messages=messages_multi_array,
334
+ tools=tools,
335
+ temperature=0.0,
336
+ )
337
+
338
+ assert response_multi_array is not None
339
+ assert response_multi_array.choices[0].message is not None
340
+
341
+
342
MODEL_NAME = "openai-community/gpt2"
343
MODEL_NAME_SHORT = "gpt2"
344
CHAT_TEMPLATE = "Dummy chat template for testing {}"
vllm/entrypoints/harmony_utils.py
+7
@@ -256,6 +256,13 @@ def parse_chat_input(chat_msg) -> list[Message]:
256
if role == "tool":
257
name = chat_msg.get("name", "")
258
content = chat_msg.get("content", "") or ""
259
+ if isinstance(content, list):
260
+ # Handle array format for tool message content
261
+ # by concatenating all text parts.
262
+ content = "".join(
263
+ item.get("text", "") for item in content
264
+ if isinstance(item, dict) and item.get("type") == "text")
265
+
266
msg = Message.from_author_and_content(
267
Author.new(Role.TOOL, f"functions.{name}"), content
268
).with_channel("commentary")