Python: Fix OpenAI Responses streaming to propagate created_at from final response.completed event (#5382)

* Fix streaming response losing created_at from response.completed event (#5347)

The streaming path in _parse_chunk_from_openai did not extract created_at
from the response.completed event, unlike the non-streaming path in
_parse_responses_response. This caused durabletask persistence warnings
when created_at was None.

Extract created_at in the response.completed case and pass it to the
returned ChatResponseUpdate.

Also fix pre-existing pyright errors for optional orjson import in sample
files.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix orjson import suppression to use pyright instead of mypy (#5347)

Replace `# type: ignore[import-not-found]` with
`# pyright: ignore[reportMissingImports]` on optional orjson imports
in conversation sample files, matching the repo's Pyright strict
configuration.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Evan Mattson
2026-04-22 15:19:31 +09:00
committed by GitHub
Unverified
parent 9e915b36b6
commit ea3320d39f
4 changed files with 31 additions and 2 deletions
@@ -2028,6 +2028,7 @@ class RawOpenAIChatClient( # type: ignore[misc]
local_shell_tool_name = self._get_local_shell_tool_name(options.get("tools"))
conversation_id: str | None = None
response_id: str | None = None
created_at: str | None = None
continuation_token: OpenAIContinuationToken | None = None
model = self.model
match event.type:
@@ -2209,6 +2210,9 @@ class RawOpenAIChatClient( # type: ignore[misc]
response_id = event.response.id
conversation_id = self._get_conversation_id(event.response, options.get("store"))
model = event.response.model
created_at = datetime.fromtimestamp(event.response.created_at, tz=timezone.utc).strftime(
"%Y-%m-%dT%H:%M:%S.%fZ"
)
if event.response.usage:
usage = self._parse_usage_from_openai(event.response.usage)
if usage:
@@ -2589,6 +2593,7 @@ class RawOpenAIChatClient( # type: ignore[misc]
response_id=response_id,
role="assistant",
model=model,
created_at=created_at,
continuation_token=continuation_token,
additional_properties=metadata,
raw_representation=event,
@@ -2192,6 +2192,7 @@ def test_streaming_chunk_with_usage_only() -> None:
mock_event.response.id = "resp_usage"
mock_event.response.model = "test-model"
mock_event.response.conversation = None
mock_event.response.created_at = 1000000000.0
mock_event.response.usage = MagicMock()
mock_event.response.usage.input_tokens = 50
mock_event.response.usage.output_tokens = 25
@@ -4449,6 +4450,7 @@ def test_streaming_response_completed_no_continuation_token() -> None:
mock_event.response.conversation = MagicMock()
mock_event.response.conversation.id = "conv_done"
mock_event.response.model = "test-model"
mock_event.response.created_at = 1000000000.0
mock_event.response.usage = None
update = client._parse_chunk_from_openai(mock_event, chat_options, function_call_ids)
@@ -4456,6 +4458,28 @@ def test_streaming_response_completed_no_continuation_token() -> None:
assert update.continuation_token is None
def test_streaming_response_completed_sets_created_at() -> None:
"""Test that response.completed sets created_at on the ChatResponseUpdate."""
client = OpenAIChatClient(model="test-model", api_key="test-key")
chat_options: dict[str, Any] = {}
function_call_ids: dict[int, tuple[str, str]] = {}
mock_event = MagicMock()
mock_event.type = "response.completed"
mock_event.response = MagicMock()
mock_event.response.id = "resp_created"
mock_event.response.conversation = MagicMock()
mock_event.response.conversation.id = "conv_created"
mock_event.response.model = "test-model"
mock_event.response.created_at = 1000000000.0
mock_event.response.usage = None
update = client._parse_chunk_from_openai(mock_event, chat_options, function_call_ids)
assert update.created_at is not None
assert update.created_at == "2001-09-09T01:46:40.000000Z"
def test_map_chat_to_agent_update_preserves_continuation_token() -> None:
"""Test that map_chat_to_agent_update propagates continuation_token."""
from agent_framework._types import map_chat_to_agent_update
@@ -21,7 +21,7 @@ from dotenv import load_dotenv
from pydantic import Field
try:
import orjson
import orjson # pyright: ignore[reportMissingImports]
except ImportError:
orjson = None
@@ -22,7 +22,7 @@ from dotenv import load_dotenv
from pydantic import Field
try:
import orjson
import orjson # pyright: ignore[reportMissingImports]
except ImportError:
orjson = None