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
- A developer defines tool inputs using
TypedDict (common in codebases avoiding heavy Pydantic usage) and expects the @tool decorator to just work.
- A tool function mixes
TypedDict, primitive, and Pydantic inputs: def my_tool(arg1: MyTypedDict, arg2: str, arg3: MyPydanticModel).
- 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
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
Feature Description
Extend the
@tooldecorator to supportTypedDictas input and output types, in addition to the existingpydantic.BaseModelsupport. The decorator should automatically generate valid Pydantic model types for input and output schemas regardless of whether the user providesTypedDict,pydantic.BaseModel, or primitive types.Motivation
Currently the
@tooldecorator only supportspydantic.BaseModelfor structured inputs/outputs. Python'sTypedDictis a lightweight, widely-used alternative for defining structured types — especially common in codebases that don't want a Pydantic dependency in their domain layer. SupportingTypedDictlowers 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
TypedDictis used as a type annotation on a@tool-decorated function (input or output).TypedDictinto an equivalentpydantic.BaseModelat decoration time so the generated JSON schema remains valid and consistent.TypedDictmixed with primitives, mixed with Pydantic models, and nestedTypedDictstructures.Alternatives Considered
TypedDictto Pydantic models — adds friction and boilerplate.dataclasses— less common in typed-dict-heavy codebases and still doesn't coverTypedDict.Use Cases
TypedDict(common in codebases avoiding heavy Pydantic usage) and expects the@tooldecorator to just work.TypedDict, primitive, and Pydantic inputs:def my_tool(arg1: MyTypedDict, arg2: str, arg3: MyPydanticModel).TypedDictand the framework needs to produce a valid output schema for downstream consumption.Example Usage
Implementation Details
TypedDictsubclasses (check viatyping.is_typeddictortyping_extensions.is_typeddict).TypedDict(including nested ones) into dynamically-createdpydantic.BaseModelsubclasses usingpydantic.create_modelor equivalent.Required/NotRequiredfield markers fromTypedDictare respected in the generated Pydantic model (mapping to required vs optional fields).TypedDicttypes are preserved for the user's runtime usage.Key Test Cases
TypedDictinputs mixed with primitive and Pydantic inputs:def my_tool(arg1: TypedDictModel, arg2: str, arg3: PydanticModel)TypedDictas an inputTypedDictas an outputTypedDictas an outputTypedDicttypesBreaking Changes
Additional Context
Reference implementation: [OpenAI Agents SDK — Function Tools](https://openai.github.io/openai-agents-python/tools/#function-tools) supports
TypedDictin the same position.Related Issues