Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion python/copilot/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@
import json
from collections.abc import Awaitable, Callable
from dataclasses import dataclass, field
from datetime import date, datetime, time
from decimal import Decimal
from enum import Enum
from typing import TYPE_CHECKING, Any, Literal, TypeVar, get_type_hints, overload
from uuid import UUID

from pydantic import BaseModel, ValidationError

Expand Down Expand Up @@ -363,10 +367,18 @@ def _normalize_result(result: Any) -> ToolResult:
result_type="success",
)

# Everything else gets JSON-serialized (with Pydantic model support)
# Everything else gets JSON-serialized (with common Python and Pydantic values)
def default(obj: Any) -> Any:
if isinstance(obj, BaseModel):
return obj.model_dump(mode="json")
if isinstance(obj, (date, datetime, time)):
return obj.isoformat()
if isinstance(obj, (Decimal, UUID)):
return str(obj)
if isinstance(obj, Enum):
return obj.value
if isinstance(obj, set):
return list(obj)
raise TypeError(f"Object of type {type(obj).__name__} is not JSON serializable")

try:
Expand Down
33 changes: 33 additions & 0 deletions python/test_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,39 @@ class Record(BaseModel):
assert set(parsed["tags"]) == {"python", "sdk"}
assert result.result_type == "success"

def test_plain_dict_with_non_primitive_fields_is_serialized(self):
from datetime import date, datetime, time
from decimal import Decimal
from enum import Enum
from uuid import UUID

class Status(Enum):
ACTIVE = "active"

result = _normalize_result(
{
"id": UUID("12345678-1234-5678-1234-567812345678"),
"created": datetime(2026, 1, 15, 10, 30, 0),
"day": date(2026, 1, 15),
"at": time(10, 30, 0),
"score": Decimal("99.5"),
"status": Status.ACTIVE,
"tags": {"python", "sdk"},
}
)
parsed = json.loads(result.text_result_for_llm)
assert parsed == {
"id": "12345678-1234-5678-1234-567812345678",
"created": "2026-01-15T10:30:00",
"day": "2026-01-15",
"at": "10:30:00",
"score": "99.5",
"status": "active",
"tags": parsed["tags"],
}
assert set(parsed["tags"]) == {"python", "sdk"}
assert result.result_type == "success"

def test_raises_for_unserializable_value(self):
# Functions cannot be JSON serialized
with pytest.raises(TypeError, match="Failed to serialize"):
Expand Down