Skip to content

Conversation

@jordanalexmeyer
Copy link
Contributor

@jordanalexmeyer jordanalexmeyer commented Nov 20, 2025

Fix: Add proper serialization support for FieldMetadata aliases in UncheckedBaseModel

Problem

The Python SDK uses custom FieldMetadata annotations for field aliasing (e.g., workflow_runworkflowRun), but these aliases were not being respected during serialization. This caused two critical issues:

  1. dict(by_alias=True) - Returned snake_case field names instead of camelCase, and datetime objects weren't JSON-serializable
  2. model_dump_json(by_alias=True) - Serialized datetimes correctly but still returned snake_case field names

This prevented users from:

  • Serializing responses to match API format (camelCase)
  • Saving workflow run data to S3 or external systems
  • Round-tripping data through the API

User Impact: Customers reported they couldn't use model_dump_json(by_alias=True) to get properly formatted JSON output (reported by Ryan O. on Nov 10th)

Solution

Added custom serialization methods to UncheckedBaseModel that bridge the gap between Pydantic's native serialization and our custom FieldMetadata alias system:

  1. Override dict() - Applies FieldMetadata aliases when by_alias=True
  2. Override model_dump_json() - Serializes with aliases AND handles datetime conversion
  3. Add _apply_field_aliases() - Recursively transforms snake_case → camelCase using get_field_to_alias_mapping()
  4. Add _json_serializer() - Helper function for datetime/UUID serialization

Changes Made

File: extend_ai/core/unchecked_base_model.py

  • Added dict() method override (lines 127-144)
  • Added model_dump() method override (lines 146-156)
  • Added model_dump_json() method override (lines 158-171)
  • Added _apply_field_aliases() helper method (lines 173-220)
  • Added _json_serializer() helper function (lines 419-426)

Testing

Verified with real workflow run data:

workflow = WorkflowRun.construct(**workflow_data)

# ✅ dict(by_alias=True) returns camelCase
result = workflow.dict(by_alias=True)
assert 'initialRunAt' in result  # camelCase ✓
assert 'initial_run_at' not in result  # no snake_case ✓

# ✅ model_dump_json(by_alias=True) returns camelCase JSON with serialized dates
json_str = workflow.model_dump_json(by_alias=True)
data = json.loads(json_str)
assert 'initialRunAt' in data  # camelCase ✓
assert isinstance(data['initialRunAt'], str)  # datetime serialized ✓

Breaking Changes

None. This is purely additive - existing behavior is preserved when by_alias=False (default).

Notes

  • This is a temporary fix until we can update the Fern code generator to use native Pydantic Field(alias=...)
  • The fix leverages existing get_field_to_alias_mapping() from serialization.py
  • Handles nested models and lists recursively
  • Maintains backward compatibility with Pydantic v1 and v2

Fixes: Issue with model_dump_json(by_alias=True) not respecting FieldMetadata aliases
Related: Customer feedback from Ryan O. (Nov 10th)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants