[Bugfix] Default missing detail for Responses API input images (#57241)
Signed-off-by: Yifan Zong <yzong@redhat.com> Co-authored-by: Ben Browning <56071+bbrowning@users.noreply.github.com>
yzong-rh committed
Sep 25, 2026 at 17:08 UTC
b6761e8ded57ef85b708f34af8cab1649eae1069
2 files changed
+57
-2
tests/tool_use/test_responses_request_validations.py
+21
@@ -187,6 +187,27 @@ def test_responses_request_empty_tools_named_tool_choice():
187
)
188
189
190
+@pytest.mark.parametrize("detail", [None, "low"], ids=["default", "explicit"])
191
+def test_input_image_detail_defaults_or_preserves(detail: str | None) -> None:
192
+ image = {
193
+ "type": "input_image",
194
+ "image_url": "https://example.com/image.png",
195
+ }
196
+ if detail is not None:
197
+ image["detail"] = detail
198
+
199
+ item = {
200
+ "role": "user",
201
+ "content": [image],
202
+ }
203
+
204
+ request = ResponsesRequest.model_validate({"input": [item]})
205
+
206
+ assert request.input[0]["content"][0]["detail"] == (
207
+ "auto" if detail is None else detail
208
+ )
209
+
210
+
211
# Regression tests for parallel_tool_calls=null crash in Responses API
212
# (from_request() passed None to ResponsesResponse.parallel_tool_calls,
213
# a non-optional bool field, causing a Pydantic ValidationError during
vllm/entrypoints/openai/responses/protocol.py
+36
-2
@@ -4,7 +4,7 @@
4
# Adapted from
5
# https://github.com/lm-sys/FastChat/blob/168ccc29d3f7edc50823016105c024fe2282732a/fastchat/protocol/openai_api_protocol.py
6
import time
7
-from typing import Any, Literal, TypeAlias
7
+from typing import Annotated, Any, Literal, TypeAlias
8
9
from openai.types.responses import (
10
ResponseCodeInterpreterCallCodeDeltaEvent,
@@ -49,6 +49,7 @@ from openai.types.responses.tool import Tool
49
from openai.types.shared import Metadata, Reasoning
50
from openai_harmony import Message as OpenAIHarmonyMessage
51
from pydantic import (
52
+ BeforeValidator,
53
Field,
54
ValidationError,
55
field_serializer,
@@ -135,6 +136,31 @@ ResponseInputOutputMessage: TypeAlias = (
136
ResponseInputOutputItem: TypeAlias = ResponseInputItemParam | ResponseOutputItem
137
138
139
+def _default_input_image_details(value: Any) -> Any:
140
+ """Set the API default for input images before SDK type validation."""
141
+ if not isinstance(value, dict):
142
+ return value
143
+
144
+ content = value.get("content")
145
+ if not isinstance(content, list):
146
+ return value
147
+
148
+ new_content = []
149
+ changed = False
150
+ for part in content:
151
+ new_part = part
152
+ if (
153
+ isinstance(part, dict)
154
+ and part.get("type") == "input_image"
155
+ and "detail" not in part
156
+ ):
157
+ new_part = {**part, "detail": "auto"}
158
+ changed = True
159
+ new_content.append(new_part)
160
+
161
+ return {**value, "content": new_content} if changed else value
162
+
163
+
164
class ResponsesRequest(OpenAIBaseModel):
165
# Ordered by official OpenAI API documentation
166
# https://platform.openai.com/docs/api-reference/responses/create
@@ -152,7 +178,15 @@ class ResponsesRequest(OpenAIBaseModel):
178
]
179
| None
180
) = None
155
- input: str | list[ResponseInputOutputItem]
181
+ input: (
182
+ str
183
+ | list[
184
+ Annotated[
185
+ ResponseInputOutputItem,
186
+ BeforeValidator(_default_input_image_details),
187
+ ]
188
+ ]
189
+ )
190
instructions: str | None = None
191
max_output_tokens: int | None = None
192
max_tool_calls: int | None = None