fix(observability): handle datetime serialization in tool results (#2248)

Fixes #2219

Adds default=str to json.dumps() calls to handle non-JSON-serializable
types like datetime objects in tool function results.

Co-authored-by: kishikawa-hayato <84244732+HerBest-max@users.noreply.github.com>
This commit is contained in:
Hayato Kishikawa
2025-11-20 09:50:45 -08:00
committed by GitHub
Unverified
parent 6ae32f007d
commit f99dca033f
2 changed files with 29 additions and 3 deletions
@@ -1474,10 +1474,10 @@ def _to_otel_part(content: "Contents") -> dict[str, Any] | None:
elif isinstance(item, BaseModel):
res.append(item.model_dump(exclude_none=True))
else:
res.append(json.dumps(item))
response = json.dumps(res)
res.append(json.dumps(item, default=str))
response = json.dumps(res, default=str)
else:
response = json.dumps(content.result)
response = json.dumps(content.result, default=str)
return {"type": "tool_call_response", "id": content.call_id, "response": response}
case _:
# GenericPart in otel output messages json spec.
@@ -0,0 +1,26 @@
# Copyright (c) Microsoft. All rights reserved.
"""Test datetime serialization in observability telemetry."""
import json
from datetime import datetime
from agent_framework._types import FunctionResultContent
from agent_framework.observability import _to_otel_part
def test_datetime_in_tool_results() -> None:
"""Test that tool results with datetime values are serialized.
Reproduces issue #2219 where datetime objects caused TypeError.
"""
content = FunctionResultContent(
call_id="test-call",
result={"timestamp": datetime(2025, 11, 16, 10, 30, 0)},
)
result = _to_otel_part(content)
parsed = json.loads(result["response"])
# Datetime should be converted to string
assert isinstance(parsed["timestamp"], str)