From 16fb844ed8b9562538de1de80ff32f4cf3b579ce Mon Sep 17 00:00:00 2001 From: "codeflash-ai[bot]" <148906541+codeflash-ai[bot]@users.noreply.github.com> Date: Fri, 24 Oct 2025 06:09:54 +0000 Subject: [PATCH] Optimize AsyncV1SocketClient._handle_json_message The optimization introduces **TypeAdapter caching** using `functools.lru_cache` to eliminate the expensive repeated creation of `pydantic.TypeAdapter` objects. **Key change:** - Added `_get_type_adapter()` function with `@functools.lru_cache(maxsize=32)` that caches TypeAdapter instances by type - Replaced direct `pydantic.TypeAdapter(type_)` calls with cached `_get_type_adapter(type_)` calls **Why this speeds up the code:** The line profiler shows that `pydantic.TypeAdapter(type_)` construction was taking **76.3% of execution time** (10.06ms out of 13.19ms total). TypeAdapter creation involves significant internal setup including type analysis, validation schema generation, and metadata processing. By caching these expensive objects, subsequent calls with the same type reuse the pre-built adapter instead of recreating it. **Performance impact:** - Original: 10.06ms spent on TypeAdapter creation (76.3% of total time) - Optimized: 3.39ms spent on cached adapter retrieval (53.1% of total time) - **67% reduction** in TypeAdapter overhead, leading to overall **315% speedup** This optimization is particularly effective for WebSocket message handling scenarios where the same message types (like `V1SocketClientResponse`) are parsed repeatedly, as shown in the test cases. The LRU cache with maxsize=32 provides excellent hit rates for typical usage patterns while preventing unbounded memory growth. --- src/deepgram/core/pydantic_utilities.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/deepgram/core/pydantic_utilities.py b/src/deepgram/core/pydantic_utilities.py index 8906cdfa..b2bf4731 100644 --- a/src/deepgram/core/pydantic_utilities.py +++ b/src/deepgram/core/pydantic_utilities.py @@ -2,11 +2,14 @@ # nopycln: file import datetime as dt +import functools from collections import defaultdict from typing import Any, Callable, ClassVar, Dict, List, Mapping, Optional, Set, Tuple, Type, TypeVar, Union, cast import pydantic +from deepgram.core.serialization import convert_and_respect_annotation_metadata + IS_PYDANTIC_V2 = pydantic.VERSION.startswith("2.") if IS_PYDANTIC_V2: @@ -39,7 +42,7 @@ def parse_obj_as(type_: Type[T], object_: Any) -> T: dealiased_object = convert_and_respect_annotation_metadata(object_=object_, annotation=type_, direction="read") if IS_PYDANTIC_V2: - adapter = pydantic.TypeAdapter(type_) # type: ignore[attr-defined] + adapter = _get_type_adapter(type_) return adapter.validate_python(dealiased_object) return pydantic.parse_obj_as(type_, dealiased_object) @@ -256,3 +259,9 @@ def _get_field_default(field: PydanticField) -> Any: return None return value return value + + +# Cache TypeAdapter creation for improved performance +@functools.lru_cache(maxsize=32) +def _get_type_adapter(type_: Type[Any]) -> Any: + return pydantic.TypeAdapter(type_) # type: ignore[attr-defined]