Skip to content
Merged
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* `.stream()` and `.stream_async()` now support a `data_model` parameter for structured data extraction while streaming. (#262)
* `.to_solver()` now supports a `data_model` parameter for structured data extraction in evals. When provided, the solver uses `.chat_structured()` instead of `.chat()` and outputs JSON-serialized data. (#264)

### Bug fixes

* Fixed `ContentToolResult` with an `error` not being JSON serializable. When a tool call failed, calling `.get_turns()` followed by `.model_dump_json()` would raise a `PydanticSerializationError`. (#267)

## [0.15.0] - 2026-01-06

### New features
Expand Down
22 changes: 21 additions & 1 deletion chatlas/_content.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from typing import TYPE_CHECKING, Any, Literal, Optional, Union, cast

import orjson
from pydantic import BaseModel, ConfigDict
from pydantic import BaseModel, ConfigDict, field_serializer, field_validator

from ._typing_extensions import TypedDict

Expand Down Expand Up @@ -363,6 +363,26 @@ class ContentToolResult(Content):
request: Optional[ContentToolRequest] = None
content_type: ContentTypeEnum = "tool_result"

@field_serializer("error")
@classmethod
def serialize_error(cls, v: Optional[Exception]) -> Optional[str]:
"""Serialize Exception to string for JSON compatibility."""
if v is None:
return None
return str(v)

@field_validator("error", mode="before")
@classmethod
def validate_error(cls, v: Any) -> Optional[Exception]:
"""Accept string or Exception for error field."""
if v is None:
return None
if isinstance(v, Exception):
return v
if isinstance(v, str):
return Exception(v)
return Exception(str(v))
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The final return statement at line 384 handles arbitrary input types by converting them to Exception, but this creates an unclear error path. If the value is not None, Exception, or str, it would be clearer to raise a validation error rather than silently converting unexpected types. Consider either adding explicit type checking or documenting why arbitrary types should be accepted and converted.

Suggested change
return Exception(str(v))
raise TypeError(
f"Invalid type for 'error': expected None, Exception, or str, got {type(v).__name__}"
)

Copilot uses AI. Check for mistakes.

@property
def id(self):
if not self.request:
Expand Down
Loading