mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Python: Fix: AgentRunResponse.created_at returns local datetime labeled as UTC (#2590)
* Initial plan * Fix created_at to use UTC datetime and add unit tests Co-authored-by: moonbox3 <35585003+moonbox3@users.noreply.github.com> * Address code review feedback: move datetime imports to top of test files Co-authored-by: moonbox3 <35585003+moonbox3@users.noreply.github.com> * Add UTC timestamp test for OpenAIResponsesClient._create_response_content Co-authored-by: moonbox3 <35585003+moonbox3@users.noreply.github.com> * Fix lint issue --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: moonbox3 <35585003+moonbox3@users.noreply.github.com> Co-authored-by: Evan Mattson <evan.mattson@microsoft.com>
This commit is contained in:
committed by
GitHub
Unverified
parent
ae06c9e4bb
commit
292a2078fa
@@ -2,6 +2,7 @@
|
||||
|
||||
import base64
|
||||
from collections.abc import AsyncIterable
|
||||
from datetime import datetime, timezone
|
||||
from typing import Any
|
||||
|
||||
import pytest
|
||||
@@ -936,6 +937,52 @@ def test_agent_run_response_update_str_method(text_content: TextContent) -> None
|
||||
assert str(update) == "Test content"
|
||||
|
||||
|
||||
def test_agent_run_response_update_created_at() -> None:
|
||||
"""Test that AgentRunResponseUpdate properly handles created_at timestamps."""
|
||||
# Test with a properly formatted UTC timestamp
|
||||
utc_timestamp = "2024-12-01T00:31:30.000000Z"
|
||||
update = AgentRunResponseUpdate(
|
||||
contents=[TextContent(text="test")],
|
||||
role=Role.ASSISTANT,
|
||||
created_at=utc_timestamp,
|
||||
)
|
||||
assert update.created_at == utc_timestamp
|
||||
assert update.created_at.endswith("Z"), "Timestamp should end with 'Z' for UTC"
|
||||
|
||||
# Verify that we can generate a proper UTC timestamp
|
||||
now_utc = datetime.now(tz=timezone.utc)
|
||||
formatted_utc = now_utc.strftime("%Y-%m-%dT%H:%M:%S.%fZ")
|
||||
update_with_now = AgentRunResponseUpdate(
|
||||
contents=[TextContent(text="test")],
|
||||
role=Role.ASSISTANT,
|
||||
created_at=formatted_utc,
|
||||
)
|
||||
assert update_with_now.created_at == formatted_utc
|
||||
assert update_with_now.created_at.endswith("Z")
|
||||
|
||||
|
||||
def test_agent_run_response_created_at() -> None:
|
||||
"""Test that AgentRunResponse properly handles created_at timestamps."""
|
||||
# Test with a properly formatted UTC timestamp
|
||||
utc_timestamp = "2024-12-01T00:31:30.000000Z"
|
||||
response = AgentRunResponse(
|
||||
messages=[ChatMessage(role=Role.ASSISTANT, text="Hello")],
|
||||
created_at=utc_timestamp,
|
||||
)
|
||||
assert response.created_at == utc_timestamp
|
||||
assert response.created_at.endswith("Z"), "Timestamp should end with 'Z' for UTC"
|
||||
|
||||
# Verify that we can generate a proper UTC timestamp
|
||||
now_utc = datetime.now(tz=timezone.utc)
|
||||
formatted_utc = now_utc.strftime("%Y-%m-%dT%H:%M:%S.%fZ")
|
||||
response_with_now = AgentRunResponse(
|
||||
messages=[ChatMessage(role=Role.ASSISTANT, text="Hello")],
|
||||
created_at=formatted_utc,
|
||||
)
|
||||
assert response_with_now.created_at == formatted_utc
|
||||
assert response_with_now.created_at.endswith("Z")
|
||||
|
||||
|
||||
# region ErrorContent
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
from copy import deepcopy
|
||||
from datetime import datetime, timezone
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import pytest
|
||||
@@ -370,3 +371,75 @@ async def test_get_streaming_no_stream(
|
||||
messages=chat_history,
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
# region UTC Timestamp Tests
|
||||
|
||||
|
||||
def test_chat_response_created_at_uses_utc(openai_unit_test_env: dict[str, str]):
|
||||
"""Test that ChatResponse.created_at uses UTC timestamp, not local time.
|
||||
|
||||
This is a regression test for the issue where created_at was using local time
|
||||
but labeling it as UTC (with 'Z' suffix).
|
||||
"""
|
||||
from agent_framework import ChatOptions
|
||||
|
||||
# Use a specific Unix timestamp: 1733011890 = 2024-12-01T00:31:30Z (UTC)
|
||||
# This ensures we test that the timestamp is actually converted to UTC
|
||||
utc_timestamp = 1733011890
|
||||
|
||||
mock_response = ChatCompletion(
|
||||
id="test_id",
|
||||
choices=[
|
||||
Choice(index=0, message=ChatCompletionMessage(content="test", role="assistant"), finish_reason="stop")
|
||||
],
|
||||
created=utc_timestamp,
|
||||
model="test",
|
||||
object="chat.completion",
|
||||
)
|
||||
|
||||
client = OpenAIChatClient()
|
||||
response = client._create_chat_response(mock_response, ChatOptions())
|
||||
|
||||
# Verify that created_at is correctly formatted as UTC
|
||||
assert response.created_at is not None
|
||||
assert response.created_at.endswith("Z"), "Timestamp should end with 'Z' for UTC"
|
||||
|
||||
# Parse the timestamp and verify it matches UTC time
|
||||
expected_utc_time = datetime.fromtimestamp(utc_timestamp, tz=timezone.utc)
|
||||
expected_formatted = expected_utc_time.strftime("%Y-%m-%dT%H:%M:%S.%fZ")
|
||||
assert response.created_at == expected_formatted, (
|
||||
f"Expected UTC timestamp {expected_formatted}, got {response.created_at}"
|
||||
)
|
||||
|
||||
|
||||
def test_chat_response_update_created_at_uses_utc(openai_unit_test_env: dict[str, str]):
|
||||
"""Test that ChatResponseUpdate.created_at uses UTC timestamp, not local time.
|
||||
|
||||
This is a regression test for the issue where created_at was using local time
|
||||
but labeling it as UTC (with 'Z' suffix).
|
||||
"""
|
||||
# Use a specific Unix timestamp: 1733011890 = 2024-12-01T00:31:30Z (UTC)
|
||||
utc_timestamp = 1733011890
|
||||
|
||||
mock_chunk = ChatCompletionChunk(
|
||||
id="test_id",
|
||||
choices=[ChunkChoice(index=0, delta=ChunkChoiceDelta(content="test", role="assistant"), finish_reason="stop")],
|
||||
created=utc_timestamp,
|
||||
model="test",
|
||||
object="chat.completion.chunk",
|
||||
)
|
||||
|
||||
client = OpenAIChatClient()
|
||||
response_update = client._create_chat_response_update(mock_chunk)
|
||||
|
||||
# Verify that created_at is correctly formatted as UTC
|
||||
assert response_update.created_at is not None
|
||||
assert response_update.created_at.endswith("Z"), "Timestamp should end with 'Z' for UTC"
|
||||
|
||||
# Parse the timestamp and verify it matches UTC time
|
||||
expected_utc_time = datetime.fromtimestamp(utc_timestamp, tz=timezone.utc)
|
||||
expected_formatted = expected_utc_time.strftime("%Y-%m-%dT%H:%M:%S.%fZ")
|
||||
assert response_update.created_at == expected_formatted, (
|
||||
f"Expected UTC timestamp {expected_formatted}, got {response_update.created_at}"
|
||||
)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import asyncio
|
||||
import base64
|
||||
import os
|
||||
from datetime import datetime, timezone
|
||||
from typing import Annotated
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
@@ -684,6 +685,51 @@ def test_create_response_content_with_mcp_approval_request() -> None:
|
||||
assert req.function_call.additional_properties["server_label"] == "My_MCP"
|
||||
|
||||
|
||||
def test_responses_client_created_at_uses_utc(openai_unit_test_env: dict[str, str]) -> None:
|
||||
"""Test that ChatResponse from responses client uses UTC timestamp.
|
||||
|
||||
This is a regression test for the issue where created_at was using local time
|
||||
but labeling it as UTC (with 'Z' suffix).
|
||||
"""
|
||||
client = OpenAIResponsesClient()
|
||||
|
||||
# Use a specific Unix timestamp: 1733011890 = 2024-12-01T00:31:30Z (UTC)
|
||||
utc_timestamp = 1733011890
|
||||
|
||||
mock_response = MagicMock()
|
||||
mock_response.output_parsed = None
|
||||
mock_response.metadata = {}
|
||||
mock_response.usage = None
|
||||
mock_response.id = "test-id"
|
||||
mock_response.model = "test-model"
|
||||
mock_response.created_at = utc_timestamp
|
||||
|
||||
mock_message_content = MagicMock()
|
||||
mock_message_content.type = "output_text"
|
||||
mock_message_content.text = "Test response"
|
||||
mock_message_content.annotations = None
|
||||
|
||||
mock_message_item = MagicMock()
|
||||
mock_message_item.type = "message"
|
||||
mock_message_item.content = [mock_message_content]
|
||||
|
||||
mock_response.output = [mock_message_item]
|
||||
|
||||
with patch.object(client, "_get_metadata_from_response", return_value={}):
|
||||
response = client._create_response_content(mock_response, chat_options=ChatOptions()) # type: ignore
|
||||
|
||||
# Verify that created_at is correctly formatted as UTC
|
||||
assert response.created_at is not None
|
||||
assert response.created_at.endswith("Z"), "Timestamp should end with 'Z' for UTC"
|
||||
|
||||
# Parse the timestamp and verify it matches UTC time
|
||||
expected_utc_time = datetime.fromtimestamp(utc_timestamp, tz=timezone.utc)
|
||||
expected_formatted = expected_utc_time.strftime("%Y-%m-%dT%H:%M:%S.%fZ")
|
||||
assert response.created_at == expected_formatted, (
|
||||
f"Expected UTC timestamp {expected_formatted}, got {response.created_at}"
|
||||
)
|
||||
|
||||
|
||||
def test_tools_to_response_tools_with_raw_image_generation() -> None:
|
||||
"""Test that raw image_generation tool dict is handled correctly with parameter mapping."""
|
||||
client = OpenAIResponsesClient(model_id="test-model", api_key="test-key")
|
||||
|
||||
Reference in New Issue
Block a user