Skip to content

Commit ea3320d

Browse files
moonbox3CopilotCopilot
authored
Python: Fix OpenAI Responses streaming to propagate created_at from final response.completed event (microsoft#5382)
* Fix streaming response losing created_at from response.completed event (microsoft#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 (microsoft#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>
1 parent 9e915b3 commit ea3320d

4 files changed

Lines changed: 31 additions & 2 deletions

File tree

python/packages/openai/agent_framework_openai/_chat_client.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2028,6 +2028,7 @@ def _parse_chunk_from_openai(
20282028
local_shell_tool_name = self._get_local_shell_tool_name(options.get("tools"))
20292029
conversation_id: str | None = None
20302030
response_id: str | None = None
2031+
created_at: str | None = None
20312032
continuation_token: OpenAIContinuationToken | None = None
20322033
model = self.model
20332034
match event.type:
@@ -2209,6 +2210,9 @@ def _parse_chunk_from_openai(
22092210
response_id = event.response.id
22102211
conversation_id = self._get_conversation_id(event.response, options.get("store"))
22112212
model = event.response.model
2213+
created_at = datetime.fromtimestamp(event.response.created_at, tz=timezone.utc).strftime(
2214+
"%Y-%m-%dT%H:%M:%S.%fZ"
2215+
)
22122216
if event.response.usage:
22132217
usage = self._parse_usage_from_openai(event.response.usage)
22142218
if usage:
@@ -2589,6 +2593,7 @@ def _get_ann_value(key: str) -> Any:
25892593
response_id=response_id,
25902594
role="assistant",
25912595
model=model,
2596+
created_at=created_at,
25922597
continuation_token=continuation_token,
25932598
additional_properties=metadata,
25942599
raw_representation=event,

python/packages/openai/tests/openai/test_openai_chat_client.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2192,6 +2192,7 @@ def test_streaming_chunk_with_usage_only() -> None:
21922192
mock_event.response.id = "resp_usage"
21932193
mock_event.response.model = "test-model"
21942194
mock_event.response.conversation = None
2195+
mock_event.response.created_at = 1000000000.0
21952196
mock_event.response.usage = MagicMock()
21962197
mock_event.response.usage.input_tokens = 50
21972198
mock_event.response.usage.output_tokens = 25
@@ -4449,13 +4450,36 @@ def test_streaming_response_completed_no_continuation_token() -> None:
44494450
mock_event.response.conversation = MagicMock()
44504451
mock_event.response.conversation.id = "conv_done"
44514452
mock_event.response.model = "test-model"
4453+
mock_event.response.created_at = 1000000000.0
44524454
mock_event.response.usage = None
44534455

44544456
update = client._parse_chunk_from_openai(mock_event, chat_options, function_call_ids)
44554457

44564458
assert update.continuation_token is None
44574459

44584460

4461+
def test_streaming_response_completed_sets_created_at() -> None:
4462+
"""Test that response.completed sets created_at on the ChatResponseUpdate."""
4463+
client = OpenAIChatClient(model="test-model", api_key="test-key")
4464+
chat_options: dict[str, Any] = {}
4465+
function_call_ids: dict[int, tuple[str, str]] = {}
4466+
4467+
mock_event = MagicMock()
4468+
mock_event.type = "response.completed"
4469+
mock_event.response = MagicMock()
4470+
mock_event.response.id = "resp_created"
4471+
mock_event.response.conversation = MagicMock()
4472+
mock_event.response.conversation.id = "conv_created"
4473+
mock_event.response.model = "test-model"
4474+
mock_event.response.created_at = 1000000000.0
4475+
mock_event.response.usage = None
4476+
4477+
update = client._parse_chunk_from_openai(mock_event, chat_options, function_call_ids)
4478+
4479+
assert update.created_at is not None
4480+
assert update.created_at == "2001-09-09T01:46:40.000000Z"
4481+
4482+
44594483
def test_map_chat_to_agent_update_preserves_continuation_token() -> None:
44604484
"""Test that map_chat_to_agent_update propagates continuation_token."""
44614485
from agent_framework._types import map_chat_to_agent_update

python/samples/02-agents/conversations/file_history_provider.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from pydantic import Field
2222

2323
try:
24-
import orjson
24+
import orjson # pyright: ignore[reportMissingImports]
2525
except ImportError:
2626
orjson = None
2727

python/samples/02-agents/conversations/file_history_provider_conversation_persistence.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
from pydantic import Field
2323

2424
try:
25-
import orjson
25+
import orjson # pyright: ignore[reportMissingImports]
2626
except ImportError:
2727
orjson = None
2828

0 commit comments

Comments
 (0)