Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,99 @@

import pytest
from openai.types.responses import ResponseFunctionToolCall, ResponseOutputMessage
from openai.types.responses.response_item import AdditionalTools

from vllm.entrypoints.openai.responses.protocol import ResponsesRequest


def make_function_tool(name: str) -> dict:
return {
"type": "function",
"name": name,
"description": f"Call {name}.",
"parameters": {
"type": "object",
"properties": {},
},
}


def test_additional_tools_are_promoted_out_of_input():
"""Additional tools configure the request and are not chat messages."""
request = ResponsesRequest(
model="gpt-oss",
input=[
{
"id": "at_123",
"type": "additional_tools",
"role": "developer",
"tools": [make_function_tool("exec")],
},
{
"type": "message",
"role": "user",
"content": [{"type": "input_text", "text": "Run pwd"}],
},
],
tool_choice="required",
)

assert len(request.input) == 1
assert request.input[0]["type"] == "message"
assert [tool.name for tool in request.tools] == ["exec"]
assert request.tool_choice == "required"


def test_additional_tools_extend_top_level_tools_in_input_order():
request = ResponsesRequest(
model="gpt-oss",
tools=[make_function_tool("top_level")],
input=[
{
"id": "at_123",
"type": "additional_tools",
"role": "developer",
"tools": [make_function_tool("first_additional")],
},
{
"id": "at_456",
"type": "additional_tools",
"role": "developer",
"tools": [make_function_tool("second_additional")],
},
{
"type": "message",
"role": "user",
"content": [{"type": "input_text", "text": "Hello"}],
},
],
)

assert len(request.input) == 1
assert [tool.name for tool in request.tools] == [
"top_level",
"first_additional",
"second_additional",
]


def test_typed_additional_tools_are_promoted():
additional_tools = AdditionalTools(
id="at_123",
type="additional_tools",
role="developer",
tools=[make_function_tool("exec")],
)

request = ResponsesRequest(
model="gpt-oss",
input=[additional_tools, {"role": "user", "content": "Run pwd"}],
)

assert len(request.input) == 1
assert [tool.name for tool in request.tools] == ["exec"]


def test_function_call_dict_converted_to_object():
"""Test that function_call dictionaries are correctly parsed into
ResponseFunctionToolCall objects."""
Expand Down
20 changes: 20 additions & 0 deletions vllm/entrypoints/openai/responses/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
ResponseInProgressEvent as OpenAIResponseInProgressEvent,
)
from openai.types.responses.response import IncompleteDetails, ToolChoice
from openai.types.responses.response_item import AdditionalTools
from openai.types.responses.response_reasoning_item import (
Content as ResponseReasoningTextContent,
)
Expand Down Expand Up @@ -605,6 +606,25 @@ def check_tool_usage(cls, data):
if not isinstance(data, dict):
return data

input_data = data.get("input")
if isinstance(input_data, list):
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
input_items = []
additional_tools = []
for item in input_data:
if isinstance(item, AdditionalTools):
additional_tools.extend(item.tools)
elif isinstance(item, dict) and item.get("type") == "additional_tools":
try:
additional_tools.extend(AdditionalTools(**item).tools)
except ValidationError:
input_items.append(item)
else:
input_items.append(item)

if len(input_items) != len(input_data):
data["input"] = input_items
data["tools"] = [*(data.get("tools") or []), *additional_tools]

tools = data.get("tools")
tool_choice = data.get("tool_choice", "auto")
has_tools = tools is not None and len(tools) > 0
Expand Down
Loading