Jump to content

Connect Leeroopedia MCP: Equip your AI agents to search best practices, build plans, verify code, diagnose failures, and look up hyperparameter defaults.

Implementation:Anthropics Anthropic sdk python Transform Utils

From Leeroopedia
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 base64 format

Related Pages

Used By

Uses

Page Connections

Double-click a node to navigate. Hold to expand connections.
Principle
Implementation
Heuristic
Environment