-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Python: [Bug]: response_format with Pydantic model crashes on background (Responses API) polling #5145
Description
Description
When using response_format=<PydanticModel> together with background=True (Responses API background mode), the SDK crashes with a ValidationError during polling. On each poll iteration, the SDK constructs a ChatResponse and eagerly accesses .value, which calls _parse_structured_response_value() with the current .text. While the background response is still in-progress, .text is an empty string (""), causing model_validate_json("") to fail.
This also affects the dict schema path — json.loads("") raises JSONDecodeError which is wrapped in a ValueError. Both code paths in _parse_structured_response_value are broken for background responses.
Steps to Reproduce
See code below
Root Cause
In _parse_non_streaming_response (_agents.py:1034), the AgentResponse is constructed with value=response.value. This eagerly triggers the ChatResponse.value property (_types.py:2247), which calls _parse_structured_response_value().
When the response is still in-progress (has a continuation_token), self.text is "". The function _parse_structured_response_value (_types.py:1961) unconditionally calls response_format.model_validate_json(text) on the empty string, which fails.
Suggested Fix
Guard against empty text in _parse_structured_response_value or skip parsing when continuation_token is present:
def _parse_structured_response_value(text: str, response_format: Any | None) -> Any | None:
if response_format is None:
return None
if not text: # <-- Add this guard for in-progress background responses
return None
# ... rest of existing parsing logicCurrent Workaround
Monkey-patch _parse_structured_response_value to guard against empty text:
import agent_framework._types as _af_types
_original_parse = _af_types._parse_structured_response_value
def _safe_parse(text, response_format):
if not text:
return None
return _original_parse(text, response_format)
_af_types._parse_structured_response_value = _safe_parseWith this patch applied, response_format=<PydanticModel> works correctly with background=True — .value returns None on in-progress polls and the parsed Pydantic instance once complete.
Code Sample
from pydantic import BaseModel, Field
from agent_framework import Message, AgentSession
from agent_framework.openai import OpenAIChatClient, OpenAIChatOptions
class CityList(BaseModel):
"""A list of cities."""
cities: list[str] = Field(description="List of city names")
country: str = Field(description="Country name")
client = OpenAIChatClient(...)
default_options = OpenAIChatOptions(
response_format=CityList,
background=True,
)
agent = client.as_agent(
instructions="You are a helpful assistant.",
name="extractor",
default_options=default_options,
)
session = AgentSession()
messages = [Message(role="user", contents=["List 50 Norwegian cities."])]
# Crashes here
response = await agent.run(
messages=messages,
session=session,
)
# Poll for completion
while response.continuation_token is not None:
await asyncio.sleep(2)
response = await agent.run(
session=session,
options=OpenAIChatOptions(continuation_token=response.continuation_token),
)
# response.value would work here, but we never get hereError Messages / Stack Traces
---------------------------------------------------------------------------
ValidationError Traceback (most recent call last)
Cell In[38], line 38
34 )
35
36 session = AgentSession()
37 messages = [Message(role="user", contents=["List 50 Norwegian cities."])]
---> 38 response = await agent_bg_structured.run(
39 messages=messages,
40 session=session,
41 )
File ...\agent_framework\_agents.py:955, in RawAgent.run.<locals>._run_non_streaming()
953 ctx = await _prepare_run_context()
954 response = await self._call_chat_client(ctx, stream=False)
--> 955 return await self._parse_non_streaming_response(ctx, response)
File ...\agent_framework\_agents.py:1034, in RawAgent._parse_non_streaming_response(self, context, response)
1020 raise AgentInvalidResponseException("Chat client did not return a response.")
1022 await self._finalize_response(
1023 response=response,
1024 agent_name=context["agent_name"],
(...) 1027 suppress_response_id=context["suppress_response_id"],
1028 )
1029 return AgentResponse(
1030 messages=response.messages,
1031 response_id=None if context["suppress_response_id"] else response.response_id,
1032 created_at=response.created_at,
1033 usage_details=response.usage_details,
-> 1034 value=response.value,
1035 response_format=context["chat_options"].get("response_format"),
1036 continuation_token=response.continuation_token,
1037 raw_representation=response,
1038 additional_properties=response.additional_properties,
1039 )
File ...\agent_framework\_types.py:2247, in ChatResponse.value(self)
2245 return self._value
2246 if self._response_format is not None:
-> 2247 self._value = cast(ResponseModelT, _parse_structured_response_value(self.text, self._response_format))
2248 self._value_parsed = True
2249 return self._value
File ...\agent_framework\_types.py:1961, in _parse_structured_response_value(text, response_format)
1959 return None
1960 if isinstance(response_format, type) and issubclass(response_format, BaseModel):
-> 1961 return response_format.model_validate_json(text)
1962 if isinstance(response_format, Mapping):
1963 try:
File ...\pydantic\main.py:766, in BaseModel.model_validate_json(cls, json_data, strict, extra, context, by_alias, by_name)
760 if by_alias is False and by_name is not True:
761 raise PydanticUserError(
762 'At least one of `by_alias` or `by_name` must be set to True.',
763 code='validate-by-alias-and-name-false',
764 )
--> 766 return cls.__pydantic_validator__.validate_json(
767 json_data, strict=strict, extra=extra, context=context, by_alias=by_alias, by_name=by_name
768 )
ValidationError: 1 validation error for CityList
Invalid JSON: EOF while parsing a value at line 1 column 0 [type=json_invalid, input_value='', input_type=str]
For further information visit https://errors.pydantic.dev/2.12/v/json_invalidPackage Versions
agent-framework-core: 1.0.0, agent-framework-openai: 1.0.0, fastapi[standard]: 0.135.3, pydantic: 2.12.5
Python Version
Python 3.14.3
Additional Context
No response