Skip to content

Commit ea6a2ed

Browse files
author
Anusha Gangadi
committed
[Bugfix] Fix ResponseCreatedEvent ValidationError for json_schema format in streaming
Remove `.model_dump()` from `ResponsesResponse` before passing to `ResponseCreatedEvent` and `ResponseInProgressEvent`. The `.model_dump()` serialized using Python attribute names (e.g. `schema_`) but Pydantic re-validation expected JSON alias names (e.g. `schema`), causing a ValidationError when `text.format.type: json_schema` was used with streaming. This makes `ResponseCreatedEvent`/`ResponseInProgressEvent` construction consistent with `ResponseCompletedEvent`, which already passes the object directly. Signed-off-by: Anusha Gangadi <agangadi@nvidia.com>
1 parent 3b30e61 commit ea6a2ed

File tree

2 files changed

+58
-1
lines changed

2 files changed

+58
-1
lines changed

tests/v1/entrypoints/openai/serving_responses/test_structured_output.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import json
44

55
import openai
6+
import openai.types.responses as openai_responses_types
67
import pytest
78
from pydantic import BaseModel
89

@@ -50,6 +51,62 @@ async def test_structured_output(client: openai.AsyncOpenAI):
5051
assert participants[1] == "Bob"
5152

5253

54+
@pytest.mark.asyncio
55+
async def test_structured_output_streaming(client: openai.AsyncOpenAI):
56+
"""Test streaming with json_schema format.
57+
58+
Regression test: ResponseCreatedEvent previously failed validation when
59+
json_schema format was used because .model_dump() serialized the response
60+
with Python attribute names (schema_) instead of JSON aliases (schema).
61+
"""
62+
stream = await client.responses.create(
63+
input=[
64+
{"role": "system", "content": "Extract the event information."},
65+
{
66+
"role": "user",
67+
"content": "Alice and Bob are going to a science fair on Friday.",
68+
},
69+
],
70+
text={
71+
"format": {
72+
"type": "json_schema",
73+
"name": "calendar_event",
74+
"schema": {
75+
"type": "object",
76+
"properties": {
77+
"event_name": {"type": "string"},
78+
"date": {"type": "string"},
79+
"participants": {
80+
"type": "array",
81+
"items": {"type": "string"},
82+
},
83+
},
84+
"required": ["event_name", "date", "participants"],
85+
"additionalProperties": False,
86+
},
87+
"description": "A calendar event.",
88+
"strict": True,
89+
}
90+
},
91+
stream=True,
92+
)
93+
events = [event async for event in stream]
94+
assert isinstance(events[0], openai_responses_types.ResponseCreatedEvent)
95+
assert any(
96+
isinstance(event, openai_responses_types.ResponseTextDeltaEvent)
97+
for event in events
98+
)
99+
assert isinstance(events[-1], openai_responses_types.ResponseCompletedEvent)
100+
101+
# Verify the completed response has valid structured output.
102+
completed_event = events[-1]
103+
output_text = completed_event.response.output[-1].content[0].text
104+
event = json.loads(output_text)
105+
assert "event_name" in event
106+
assert "date" in event
107+
assert "participants" in event
108+
109+
53110
@pytest.mark.asyncio
54111
async def test_structured_output_with_parse(client: openai.AsyncOpenAI):
55112
class CalendarEvent(BaseModel):

vllm/entrypoints/openai/responses/serving.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2490,7 +2490,7 @@ def _increment_sequence_number_and_return(
24902490
output=[],
24912491
status="in_progress",
24922492
usage=None,
2493-
).model_dump()
2493+
)
24942494
yield _increment_sequence_number_and_return(
24952495
ResponseCreatedEvent(
24962496
type="response.created",

0 commit comments

Comments
 (0)