From defce57d5b9087415bb71ad7362a614fba2d4917 Mon Sep 17 00:00:00 2001 From: Hamilton Truong Date: Thu, 12 Nov 2026 18:40:25 -0800 Subject: [PATCH] sdk-types: build models on first use (defer_build) Set Pydantic's defer_build on TurnkeyBaseModel so each generated model builds its validator and serializer on first use instead of at import. Only enabled on Pydantic 2.5+; older versions keep the current behavior. Co-Authored-By: Claude Opus 5.5 --- .changeset/sdk-types-defer-build.yml | 10 ++++++++++ codegen/types/generate_types.py | 7 ++++++- .../sdk-types/src/turnkey_sdk_types/generated/types.py | 6 +++++- 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 .changeset/sdk-types-defer-build.yml diff --git a/.changeset/sdk-types-defer-build.yml b/.changeset/sdk-types-defer-build.yml new file mode 100644 index 0000000..f958369 --- /dev/null +++ b/.changeset/sdk-types-defer-build.yml @@ -0,0 +1,10 @@ +type: minor +packages: [ sdk-types ] +changelog: |- + Importing `turnkey_sdk_types` is much faster and uses much less memory: each generated model now builds its validator and serializer the first time it's used, instead of all of them at import (Pydantic `defer_build`). On Pydantic 2.14, import takes about 0.2s instead of 2-3s, and memory after import drops from about 276MB to about 50MB. + + This is only enabled on Pydantic 2.5 and later. Behavior is unchanged, with a few things to know: + - The first use of each model takes slightly longer, usually a few milliseconds. Activity response models take about 20ms. + - A process that uses most of the activity response types builds a separate copy of the activity schema for each, so its peak memory can be higher than before (about 520MB with every model built, compared with about 280MB). + - On Pydantic 2.10 and earlier, `Model.model_fields[...].annotation` can show an unresolved `ForwardRef` until the model is first used. Field names, aliases, and defaults are unaffected. Call `Model.model_rebuild()` first if you inspect annotations right after import. + - A model that can't be built would now fail on first use instead of at import. Every current model builds. diff --git a/codegen/types/generate_types.py b/codegen/types/generate_types.py index fe70093..6bf6cae 100644 --- a/codegen/types/generate_types.py +++ b/codegen/types/generate_types.py @@ -441,10 +441,15 @@ def main(): output += "from __future__ import annotations\n" output += "from typing import Any, Dict, List, Optional\n" output += "from enum import Enum\n" + output += "from pydantic import VERSION as _PYDANTIC_VERSION\n" output += "from pydantic import BaseModel, Field, ConfigDict\n\n" + output += ( + "# Build models on first use, not at import. Needs Pydantic 2.5+.\n" + '_DEFER_BUILD = tuple(int(p) for p in _PYDANTIC_VERSION.split(".")[:2]) >= (2, 5)\n\n' + ) output += "\n# Base class with shared configuration\n" output += "class TurnkeyBaseModel(BaseModel):\n" - output += " model_config = ConfigDict(populate_by_name=True)\n\n" + output += " model_config = ConfigDict(populate_by_name=True, defer_build=_DEFER_BUILD)\n\n" # --- Base Types --- output += "# --- Base Types from Swagger Definitions ---\n\n" diff --git a/packages/sdk-types/src/turnkey_sdk_types/generated/types.py b/packages/sdk-types/src/turnkey_sdk_types/generated/types.py index 2483d8a..2631c16 100644 --- a/packages/sdk-types/src/turnkey_sdk_types/generated/types.py +++ b/packages/sdk-types/src/turnkey_sdk_types/generated/types.py @@ -3,12 +3,16 @@ from __future__ import annotations from typing import Any, Dict, List, Optional from enum import Enum +from pydantic import VERSION as _PYDANTIC_VERSION from pydantic import BaseModel, Field, ConfigDict +# Build models on first use, not at import. Needs Pydantic 2.5+. +_DEFER_BUILD = tuple(int(p) for p in _PYDANTIC_VERSION.split(".")[:2]) >= (2, 5) + # Base class with shared configuration class TurnkeyBaseModel(BaseModel): - model_config = ConfigDict(populate_by_name=True) + model_config = ConfigDict(populate_by_name=True, defer_build=_DEFER_BUILD) # --- Base Types from Swagger Definitions ---