fix(python): serialize datetime/UUID/Decimal/Enum/set/dataclass in plain-dict tool results - #2246
fix(python): serialize datetime/UUID/Decimal/Enum/set/dataclass in plain-dict tool results#2246rinceyuan wants to merge 1 commit into
Conversation
…result serialization Plain dicts (non-Pydantic) containing these types raise TypeError in json.dumps, causing the tool result to be silently reported as a failure to the model. Add type-specific fallbacks in _normalize_result's default() hook: - datetime/date/time -> isoformat() - UUID/Decimal -> str() - Enum -> .value - set/frozenset -> list() - dataclass -> dataclasses.asdict() Partially addresses github#2203.
|
Thanks for investigating this. I don’t think broadening Python’s serializer is the right direction. The legitimate Pydantic case is already handled by Tool calls can fail for many reasons—handler exceptions, invalid arguments, permission failures, timeouts, and serialization errors. Applications already need to display or log these failures. The current behavior fits that model: the precise reason is available in the persisted and live This PR instead turns an intentional failure into success and expands the API contract without a product requirement. I’m closing it; we can separately improve documentation if the standard failure-observation path is unclear. |
Summary
Fix silent tool failures when a
@define_toolhandler returns a plain dict (non-Pydantic) containingdatetime,UUID,Decimal,Enum,set, ordataclassvalues.Problem
_normalize_resultusesjson.dumpswith adefaulthook that only handlesBaseModel. Any other non-JSON-native type raisesTypeError, whichwrapped_handlercatches and reports to the model as:This is easy to hit (returning a dict with a timestamp or UUID) and hard to diagnose (the error is intentionally redacted).
Fix
Add type-specific fallbacks in
default():datetime/date/time.isoformat()UUID/Decimalstr()Enum.valueset/frozensetlist()dataclassdataclasses.asdict()Tests
New test
test_dict_with_non_primitive_values_is_serializedcovers all 9 types in a single plain-dict return. Existingtest_pydantic_model_with_non_primitive_fields_is_serializedcontinues to pass (Pydantic path usesmodel_dump(mode=json)).Partially addresses #2203.