Implementation:Anthropics Anthropic sdk python Transform Utils
| Knowledge Sources | |
|---|---|
| Domains | SDK_Infrastructure, Request_Serialization |
| Last Updated | 2026-02-15 12:00 GMT |
Overview
This page documents the request payload transformation utilities that convert user-provided Python dictionaries into API-ready JSON payloads by applying type-driven key aliasing, format conversions, and recursive data normalization.
Description
The _utils/_transform.py module is the critical bridge between the user-facing Pythonic API (snake_case keys, Python types) and the JSON wire format expected by the Anthropic API (camelCase/API-specific keys, serialized values). The core transform() and async_transform() functions use TypedDict type annotations to recursively process dictionaries: renaming keys via PropertyInfo(alias=...) metadata in Annotated types, formatting dates to ISO 8601, encoding file data to base64, dumping Pydantic models to JSON-compatible dicts, and handling unions/lists/iterables.
The PropertyInfo class carries per-field metadata including the alias name, format specification (iso8601, base64, custom), format template for custom date formatting, and discriminator field name. Type hints are resolved and cached via lru_cache for performance, which is important since the same type annotations are looked up repeatedly across many API calls.
Usage
These functions are called internally by the SDK before every API request. When a user calls client.messages.create(model="...", messages=[...]), the parameters TypedDict (e.g., MessageCreateParams) drives the transformation of the user's input dictionary into the format expected by the Anthropic REST API.
Code Reference
Source Location
- Repository: Anthropic SDK Python
- File:
src/anthropic/_utils/_transform.py - Lines: 1-457
- Key classes:
PropertyInfo(line 44) - Key functions:
transform()(line 92),async_transform()(line 297),maybe_transform()(line 78),async_maybe_transform()(line 284)
Signature
class PropertyInfo:
alias: str | None
format: PropertyFormat | None # Literal["iso8601", "base64", "custom"]
format_template: str | None
discriminator: str | None
def __init__(
self,
*,
alias: str | None = None,
format: PropertyFormat | None = None,
format_template: str | None = None,
discriminator: str | None = None,
) -> None
def transform(data: _T, expected_type: object) -> _T:
"""Transform dictionaries based on type information from the given type."""
def maybe_transform(data: object, expected_type: object) -> Any | None:
"""Wrapper over transform() that allows None to be passed."""
async def async_transform(data: _T, expected_type: object) -> _T:
"""Async variant of transform()."""
async def async_maybe_transform(data: object, expected_type: object) -> Any | None:
"""Async wrapper over async_transform() that allows None to be passed."""
Import
from anthropic._utils import transform, maybe_transform, async_transform, async_maybe_transform
from anthropic._utils._transform import PropertyInfo
I/O Contract
Inputs
| Name | Type | Required | Description |
|---|---|---|---|
data |
_T (typically dict) |
Yes | The user-provided data dictionary to transform (e.g., {"card_id": "abc"})
|
expected_type |
object (typically a TypedDict class) |
Yes | The type annotation that drives the transformation (e.g., MessageCreateParams)
|
Outputs
| Name | Type | Description |
|---|---|---|
transform() return |
_T |
The transformed dictionary with aliased keys and formatted values (e.g., {"cardID": "abc"})
|
maybe_transform() return |
None | Same as transform() but returns None if input is None
|
Transformation Rules
The recursive transformation engine applies these rules based on the type annotations:
Key Aliasing
class Params(TypedDict, total=False):
card_id: Required[Annotated[str, PropertyInfo(alias="cardID")]]
transform({"card_id": "<my card ID>"}, Params)
# Result: {"cardID": "<my card ID>"}
Format Conversions
| Format | Input Type | Output |
|---|---|---|
iso8601 |
date / datetime |
ISO 8601 string via .isoformat()
|
base64 |
pathlib.Path / io.IOBase |
Base64-encoded string of file contents |
custom |
date / datetime |
Formatted string via .strftime(format_template)
|
Recursive Type Handling
- TypedDict -- Each key is looked up in the type's annotations; aliased and recursively transformed
- dict -- Values are recursively transformed using the dict's value type annotation
- List / Iterable / Sequence -- Each element is recursively transformed using the inner type
- Union -- Transformation is applied against all subtypes
- pydantic.BaseModel -- Dumped via
model_dump(exclude_unset=True, mode="json") - NotGiven values -- Stripped from the output entirely
Internal Functions
def _transform_recursive(data: object, *, annotation: type, inner_type: type | None = None) -> object:
"""Core recursive transformation engine."""
def _maybe_transform_key(key: str, type_: type) -> str:
"""Apply alias from PropertyInfo metadata if present."""
def _transform_typeddict(data: Mapping[str, object], expected_type: type) -> Mapping[str, object]:
"""Transform a dict against a TypedDict's annotated fields."""
def _format_data(data: object, format_: PropertyFormat, format_template: str | None) -> object:
"""Apply format conversion (iso8601, base64, custom) to a value."""
@lru_cache(maxsize=8096)
def _get_annotated_type(type_: type) -> type | None:
"""Extract Annotated wrapper if present, with caching for performance."""
Usage Examples
Basic Key Aliasing
from typing_extensions import Annotated, Required, TypedDict
from anthropic._utils._transform import PropertyInfo, transform
class MyParams(TypedDict, total=False):
account_holder_name: Required[Annotated[str, PropertyInfo(alias="accountHolderName")]]
account_number: Required[str] # no alias -- key preserved as-is
result = transform(
{"account_holder_name": "Robert", "account_number": "123"},
MyParams,
)
# {"accountHolderName": "Robert", "account_number": "123"}
Date Formatting
from datetime import date
from typing_extensions import Annotated, TypedDict
from anthropic._utils._transform import PropertyInfo, transform
class DateParams(TypedDict, total=False):
start_date: Annotated[date, PropertyInfo(format="iso8601")]
result = transform({"start_date": date(2025, 1, 15)}, DateParams)
# {"start_date": "2025-01-15"}
How It Is Called Internally
# Inside resource methods like messages.create():
body = await async_maybe_transform(
{
"max_tokens": max_tokens,
"messages": messages,
"model": model,
},
message_create_params.MessageCreateParams,
)
Dependencies
- pydantic --
model_dump()for BaseModel serialization - anyio --
to_thread.run_sync()for running sync transforms in async context - typing_extensions --
get_args,get_type_hints, type introspection - pathlib / io / base64 -- File reading and base64 encoding for the
base64format
Related Pages
Used By
- Implementation:Anthropics_Anthropic_sdk_python_Messages_Create
- Implementation:Anthropics_Anthropic_sdk_python_Messages_Create_With_Tools
- Implementation:Anthropics_Anthropic_sdk_python_Messages_Create_With_Thinking