Skip to content

@tool Decorator: Support TypedDict in Addition to pydantic.BaseModel #60

@pk8189

Description

@pk8189

Feature Description

Extend the @tool decorator to support TypedDict as input and output types, in addition to the existing pydantic.BaseModel support. The decorator should automatically generate valid Pydantic model types for input and output schemas regardless of whether the user provides TypedDict, pydantic.BaseModel, or primitive types.

Motivation

Currently the @tool decorator only supports pydantic.BaseModel for structured inputs/outputs. Python's TypedDict is a lightweight, widely-used alternative for defining structured types — especially common in codebases that don't want a Pydantic dependency in their domain layer. Supporting TypedDict lowers the barrier to adoption and aligns with how other agent frameworks (e.g., [OpenAI Agents SDK](https://openai.github.io/openai-agents-python/tools/#function-tools)) handle tool definitions.

Proposed Solution

  • Detect when a TypedDict is used as a type annotation on a @tool-decorated function (input or output).
  • Automatically convert the TypedDict into an equivalent pydantic.BaseModel at decoration time so the generated JSON schema remains valid and consistent.
  • Handle all combinations: TypedDict mixed with primitives, mixed with Pydantic models, and nested TypedDict structures.

Alternatives Considered

  • Require users to manually convert TypedDict to Pydantic models — adds friction and boilerplate.
  • Support only dataclasses — less common in typed-dict-heavy codebases and still doesn't cover TypedDict.
  • Use raw dict + JSON Schema — loses type-safety benefits.

Use Cases

  1. A developer defines tool inputs using TypedDict (common in codebases avoiding heavy Pydantic usage) and expects the @tool decorator to just work.
  2. A tool function mixes TypedDict, primitive, and Pydantic inputs: def my_tool(arg1: MyTypedDict, arg2: str, arg3: MyPydanticModel).
  3. A tool returns a nested TypedDict and the framework needs to produce a valid output schema for downstream consumption.

Example Usage

from typing import TypedDict
from pydantic import BaseModel
from pctx import tool

class Address(TypedDict):
    street: str
    city: str
    zip_code: str

class Metadata(BaseModel):
    source: str
    confidence: float

class CustomerRecord(TypedDict):
    name: str
    address: Address  # nested TypedDict

@tool
def enrich_customer(record: CustomerRecord, query: str, meta: Metadata) -> CustomerRecord:
    """Look up and enrich a customer record."""
    ...

Implementation Details

  • At decoration time, inspect type annotations for TypedDict subclasses (check via typing.is_typeddict or typing_extensions.is_typeddict).
  • Recursively convert TypedDict (including nested ones) into dynamically-created pydantic.BaseModel subclasses using pydantic.create_model or equivalent.
  • Ensure Required / NotRequired field markers from TypedDict are respected in the generated Pydantic model (mapping to required vs optional fields).
  • The generated Pydantic models should be used for schema generation and validation; the original TypedDict types are preserved for the user's runtime usage.

Key Test Cases

  • TypedDict inputs mixed with primitive and Pydantic inputs: def my_tool(arg1: TypedDictModel, arg2: str, arg3: PydanticModel)
  • Nested TypedDict as an input
  • TypedDict as an output
  • Nested TypedDict as an output
  • Extend the integration test suite to include cases confirming the callback plumbing works end-to-end with TypedDict types

Breaking Changes

  • This feature would introduce breaking changes
  • This feature is backwards compatible

Additional Context

Reference implementation: [OpenAI Agents SDK — Function Tools](https://openai.github.io/openai-agents-python/tools/#function-tools) supports TypedDict in the same position.

Related Issues

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions