diff --git a/app/helpers/cache.py b/app/helpers/cache.py index fb6a782c..1bace6b6 100644 --- a/app/helpers/cache.py +++ b/app/helpers/cache.py @@ -1,8 +1,8 @@ import asyncio -import functools from collections import OrderedDict from collections.abc import AsyncGenerator, Awaitable from contextlib import asynccontextmanager +from functools import wraps from aiojobs import Scheduler @@ -20,9 +20,9 @@ async def get_scheduler() -> AsyncGenerator[Scheduler, None]: yield scheduler -def async_lru_cache(maxsize: int = 128): +def lru_acache(maxsize: int = 128): """ - Caches a function's return value each time it is called. + Caches an async function's return value each time it is called. If the maxsize is reached, the least recently used value is removed. """ @@ -30,7 +30,7 @@ def async_lru_cache(maxsize: int = 128): def decorator(func): cache: OrderedDict[tuple, Awaitable] = OrderedDict() - @functools.wraps(func) + @wraps(func) async def wrapper(*args, **kwargs) -> Awaitable: # Create a cache key from event loop, args and kwargs, using frozenset for kwargs to ensure hashability key = ( @@ -49,6 +49,46 @@ async def wrapper(*args, **kwargs) -> Awaitable: cache[key] = value cache.move_to_end(key) + # Remove the least recently used key if the cache is full + if len(cache) > maxsize: + cache.popitem(last=False) + + return value + + return wrapper + + return decorator + + +def lru_cache(maxsize: int = 128): + """ + Caches a sync function's return value each time it is called. + + If the maxsize is reached, the least recently used value is removed. + """ + + def decorator(func): + cache: OrderedDict[tuple, Awaitable] = OrderedDict() + + @wraps(func) + def wrapper(*args, **kwargs) -> Awaitable: + # Create a cache key from args and kwargs, using frozenset for kwargs to ensure hashability + key = ( + args, + frozenset(kwargs.items()), + ) + + if key in cache: + # Move the recently accessed key to the end (most recently used) + cache.move_to_end(key) + return cache[key] + + # Compute the value since it's not cached + value = func(*args, **kwargs) + cache[key] = value + cache.move_to_end(key) + + # Remove the least recently used key if the cache is full if len(cache) > maxsize: cache.popitem(last=False) diff --git a/app/helpers/call_events.py b/app/helpers/call_events.py index 6076dbfc..7349bccd 100644 --- a/app/helpers/call_events.py +++ b/app/helpers/call_events.py @@ -46,8 +46,8 @@ from app.models.next import NextModel from app.models.synthesis import SynthesisModel -_sms = CONFIG.sms.instance() -_db = CONFIG.database.instance() +_sms = CONFIG.sms.instance +_db = CONFIG.database.instance @tracer.start_as_current_span("on_new_call") diff --git a/app/helpers/call_llm.py b/app/helpers/call_llm.py index fa1487b1..cc62213a 100644 --- a/app/helpers/call_llm.py +++ b/app/helpers/call_llm.py @@ -49,7 +49,7 @@ extract_message_style, ) -_db = CONFIG.database.instance() +_db = CONFIG.database.instance # TODO: Refacto, this function is too long diff --git a/app/helpers/call_utils.py b/app/helpers/call_utils.py index 7293a40c..77a8face 100644 --- a/app/helpers/call_utils.py +++ b/app/helpers/call_utils.py @@ -40,7 +40,7 @@ from azure.core.exceptions import HttpResponseError, ResourceNotFoundError from noisereduce import reduce_noise -from app.helpers.cache import async_lru_cache +from app.helpers.cache import lru_acache from app.helpers.config import CONFIG from app.helpers.features import ( recognition_stt_complete_timeout_ms, @@ -71,7 +71,7 @@ r"[^\w\sÀ-ÿ'«»“”\"\"‘’''(),.!?;:\-\+_@/&€$%=]" # noqa: RUF001 ) # Sanitize text for TTS -_db = CONFIG.database.instance() +_db = CONFIG.database.instance class CallHangupException(Exception): @@ -526,7 +526,7 @@ def _detect_hangup() -> Generator[None, None, None]: raise e -@async_lru_cache() +@lru_acache() async def _use_call_client( client: CallAutomationClient, voice_id: str ) -> CallConnectionClient: diff --git a/app/helpers/config_models/ai_search.py b/app/helpers/config_models/ai_search.py index 4b72b59f..4300883c 100644 --- a/app/helpers/config_models/ai_search.py +++ b/app/helpers/config_models/ai_search.py @@ -1,4 +1,4 @@ -from functools import lru_cache +from functools import cached_property from pydantic import BaseModel, Field @@ -17,7 +17,7 @@ class AiSearchModel(BaseModel, frozen=True): strictness: float = Field(default=2, ge=0, le=5) top_n_documents: int = Field(default=5, ge=1) - @lru_cache + @cached_property def instance(self) -> ISearch: from app.helpers.config import CONFIG from app.persistence.ai_search import ( @@ -25,6 +25,6 @@ def instance(self) -> ISearch: ) return AiSearchSearch( - cache=CONFIG.cache.instance(), + cache=CONFIG.cache.instance, config=self, ) diff --git a/app/helpers/config_models/cache.py b/app/helpers/config_models/cache.py index fdc3097b..3c5229de 100644 --- a/app/helpers/config_models/cache.py +++ b/app/helpers/config_models/cache.py @@ -1,5 +1,5 @@ from enum import Enum -from functools import lru_cache +from functools import cached_property from pydantic import BaseModel, Field, SecretStr, ValidationInfo, field_validator @@ -16,7 +16,7 @@ class ModeEnum(str, Enum): class MemoryModel(BaseModel, frozen=True): max_size: int = Field(default=128, ge=10) - @lru_cache + @cached_property def instance(self) -> ICache: from app.persistence.memory import ( MemoryCache, @@ -32,7 +32,7 @@ class RedisModel(BaseModel, frozen=True): port: int = 6379 ssl: bool = True - @lru_cache + @cached_property def instance(self) -> ICache: from app.persistence.redis import ( RedisCache, @@ -68,10 +68,11 @@ def _validate_memory( raise ValueError("Memory config required") return memory + @cached_property def instance(self) -> ICache: if self.mode == ModeEnum.MEMORY: assert self.memory - return self.memory.instance() + return self.memory.instance assert self.redis - return self.redis.instance() + return self.redis.instance diff --git a/app/helpers/config_models/database.py b/app/helpers/config_models/database.py index 8b7bbf35..9dea232d 100644 --- a/app/helpers/config_models/database.py +++ b/app/helpers/config_models/database.py @@ -1,4 +1,4 @@ -from functools import lru_cache +from functools import cached_property from pydantic import BaseModel @@ -10,7 +10,7 @@ class CosmosDbModel(BaseModel, frozen=True): database: str endpoint: str - @lru_cache + @cached_property def instance(self) -> IStore: from app.helpers.config import CONFIG from app.persistence.cosmos_db import ( @@ -18,7 +18,7 @@ def instance(self) -> IStore: ) return CosmosDbStore( - cache=CONFIG.cache.instance(), + cache=CONFIG.cache.instance, config=self, ) @@ -26,5 +26,6 @@ def instance(self) -> IStore: class DatabaseModel(BaseModel): cosmos_db: CosmosDbModel + @cached_property def instance(self) -> IStore: - return self.cosmos_db.instance() + return self.cosmos_db.instance diff --git a/app/helpers/config_models/llm.py b/app/helpers/config_models/llm.py index 7538fa73..8883d407 100644 --- a/app/helpers/config_models/llm.py +++ b/app/helpers/config_models/llm.py @@ -1,7 +1,7 @@ from azure.ai.inference.aio import ChatCompletionsClient from pydantic import BaseModel -from app.helpers.cache import async_lru_cache +from app.helpers.cache import lru_acache from app.helpers.http import azure_transport from app.helpers.identity import credential @@ -14,8 +14,8 @@ class DeploymentModel(BaseModel, frozen=True): seed: int = 42 # Reproducible results temperature: float = 0.0 # Most focused and deterministic - @async_lru_cache() - async def instance(self) -> tuple[ChatCompletionsClient, "DeploymentModel"]: + @lru_acache() + async def client(self) -> tuple[ChatCompletionsClient, "DeploymentModel"]: return ChatCompletionsClient( # Reliability seed=self.seed, diff --git a/app/helpers/config_models/queue.py b/app/helpers/config_models/queue.py index 48e9f4ca..21066414 100644 --- a/app/helpers/config_models/queue.py +++ b/app/helpers/config_models/queue.py @@ -1,4 +1,4 @@ -from functools import lru_cache +from functools import cached_property from pydantic import BaseModel @@ -10,7 +10,7 @@ class QueueModel(BaseModel, frozen=True): sms_name: str training_name: str - @lru_cache + @cached_property def call(self): from app.persistence.azure_queue_storage import AzureQueueStorage @@ -19,7 +19,7 @@ def call(self): name=self.call_name, ) - @lru_cache + @cached_property def post(self): from app.persistence.azure_queue_storage import AzureQueueStorage @@ -28,7 +28,7 @@ def post(self): name=self.post_name, ) - @lru_cache + @cached_property def sms(self): from app.persistence.azure_queue_storage import AzureQueueStorage @@ -37,7 +37,7 @@ def sms(self): name=self.sms_name, ) - @lru_cache + @cached_property def training(self): from app.persistence.azure_queue_storage import AzureQueueStorage diff --git a/app/helpers/config_models/sms.py b/app/helpers/config_models/sms.py index 013dde56..929f7d8a 100644 --- a/app/helpers/config_models/sms.py +++ b/app/helpers/config_models/sms.py @@ -1,5 +1,5 @@ from enum import Enum -from functools import lru_cache +from functools import cached_property from pydantic import BaseModel, SecretStr, ValidationInfo, field_validator @@ -21,7 +21,7 @@ class CommunicationServiceModel(BaseModel, frozen=True): Model is purely empty to fit to the `ISms` interface and the "mode" enum code organization. As the Communication Services is also used as the only call interface, it is not necessary to duplicate the models. """ - @lru_cache + @cached_property def instance(self) -> ISms: from app.helpers.config import CONFIG from app.persistence.communication_services import ( @@ -36,7 +36,7 @@ class TwilioModel(BaseModel, frozen=True): auth_token: SecretStr phone_number: PhoneNumber - @lru_cache + @cached_property def instance(self) -> ISms: from app.persistence.twilio import ( TwilioSms, @@ -77,10 +77,11 @@ def _validate_twilio( raise ValueError("Twilio config required") return twilio + @cached_property def instance(self) -> ISms: if self.mode == ModeEnum.COMMUNICATION_SERVICES: assert self.communication_services - return self.communication_services.instance() + return self.communication_services.instance assert self.twilio - return self.twilio.instance() + return self.twilio.instance diff --git a/app/helpers/features.py b/app/helpers/features.py index 12339645..00fbc141 100644 --- a/app/helpers/features.py +++ b/app/helpers/features.py @@ -3,7 +3,7 @@ from azure.appconfiguration.aio import AzureAppConfigurationClient from azure.core.exceptions import ResourceNotFoundError -from app.helpers.cache import async_lru_cache +from app.helpers.cache import lru_acache from app.helpers.config import CONFIG from app.helpers.config_models.cache import MemoryModel from app.helpers.http import azure_transport @@ -233,7 +233,7 @@ async def _get(key: str, type_res: type[T]) -> T | None: ) -@async_lru_cache() +@lru_acache() async def _use_client() -> AzureAppConfigurationClient: """ Generate the App Configuration client and close it after use. diff --git a/app/helpers/http.py b/app/helpers/http.py index 96469e2a..aae6aeb3 100644 --- a/app/helpers/http.py +++ b/app/helpers/http.py @@ -9,10 +9,10 @@ from azure.core.pipeline.transport._aiohttp import AioHttpTransport from twilio.http.async_http_client import AsyncTwilioHttpClient -from app.helpers.cache import async_lru_cache +from app.helpers.cache import lru_acache -@async_lru_cache() +@lru_acache() async def _aiohttp_cookie_jar() -> DummyCookieJar: """ Create a cookie jar mock for AIOHTTP. @@ -24,7 +24,7 @@ async def _aiohttp_cookie_jar() -> DummyCookieJar: return DummyCookieJar() -@async_lru_cache() +@lru_acache() async def aiohttp_session() -> ClientSession: """ Create an AIOHTTP session. @@ -48,7 +48,7 @@ async def aiohttp_session() -> ClientSession: ) -@async_lru_cache() +@lru_acache() async def azure_transport() -> AioHttpTransport: """ Create an AIOHTTP transport, for Azure SDK. @@ -64,7 +64,7 @@ async def azure_transport() -> AioHttpTransport: ) -@async_lru_cache() +@lru_acache() async def twilio_http() -> AsyncTwilioHttpClient: """ Create a Twilio HTTP client. diff --git a/app/helpers/identity.py b/app/helpers/identity.py index e0f6796a..eed3d37f 100644 --- a/app/helpers/identity.py +++ b/app/helpers/identity.py @@ -2,11 +2,11 @@ from azure.identity.aio import DefaultAzureCredential, get_bearer_token_provider -from app.helpers.cache import async_lru_cache +from app.helpers.cache import lru_acache from app.helpers.http import azure_transport -@async_lru_cache() +@lru_acache() async def credential() -> DefaultAzureCredential: return DefaultAzureCredential( # Performance @@ -14,6 +14,6 @@ async def credential() -> DefaultAzureCredential: ) -@async_lru_cache() +@lru_acache() async def token(service: str) -> Callable[[], Awaitable[str]]: return get_bearer_token_provider(await credential(), service) diff --git a/app/helpers/llm_tools.py b/app/helpers/llm_tools.py index 08bbf61c..c1eb1277 100644 --- a/app/helpers/llm_tools.py +++ b/app/helpers/llm_tools.py @@ -20,9 +20,9 @@ from app.models.reminder import ReminderModel from app.models.training import TrainingModel -_db = CONFIG.database.instance() -_search = CONFIG.ai_search.instance() -_sms = CONFIG.sms.instance() +_db = CONFIG.database.instance +_search = CONFIG.ai_search.instance +_sms = CONFIG.sms.instance class UpdateClaimDict(TypedDict): diff --git a/app/helpers/llm_utils.py b/app/helpers/llm_utils.py index 7ef7bf29..0a610567 100644 --- a/app/helpers/llm_utils.py +++ b/app/helpers/llm_utils.py @@ -7,7 +7,7 @@ import inspect import json from collections.abc import Awaitable, Callable -from functools import lru_cache, wraps +from functools import wraps from inspect import getmembers, isfunction from textwrap import dedent from types import FunctionType @@ -25,7 +25,7 @@ from pydantic._internal._typing_extra import eval_type_lenient from pydantic.json_schema import JsonSchemaValue -from app.helpers.cache import async_lru_cache +from app.helpers.cache import lru_acache, lru_cache from app.helpers.logging import logger from app.helpers.monitoring import SpanAttributeEnum, tracer from app.models.call import CallStateModel @@ -72,7 +72,7 @@ def __init__( # noqa: PLR0913 self.tts_callback = tts_callback self.tts_client = tts_client - @async_lru_cache() + @lru_acache() async def to_openai( self, blacklist: frozenset[str], @@ -161,7 +161,7 @@ async def execute( # Enrich span SpanAttributeEnum.TOOL_RESULT.attribute(tool.content) - @lru_cache + @lru_cache() def _available_functions( self, blacklist: frozenset[str], diff --git a/app/helpers/llm_worker.py b/app/helpers/llm_worker.py index dec184f0..adb532e1 100644 --- a/app/helpers/llm_worker.py +++ b/app/helpers/llm_worker.py @@ -1,6 +1,5 @@ import json from collections.abc import AsyncGenerator, Callable -from functools import lru_cache from os import environ from typing import TypeVar @@ -30,6 +29,7 @@ wait_random_exponential, ) +from app.helpers.cache import lru_cache from app.helpers.config import CONFIG from app.helpers.config_models.llm import DeploymentModel as LlmDeploymentModel from app.helpers.features import slow_llm_for_chat @@ -345,7 +345,7 @@ def _limit_messages( # noqa: PLR0913 ] -@lru_cache # Cache results in memory as token count is done many times on the same content +@lru_cache() # Cache results in memory as token count is done many times on the same content def _count_tokens(content: str, model: str) -> int: """ Returns the number of tokens in the content, using the model's encoding. @@ -379,4 +379,4 @@ async def _use_llm( The client is either an Azure OpenAI or an OpenAI client, depending on the configuration. """ - return await CONFIG.llm.selected(is_fast).instance() + return await CONFIG.llm.selected(is_fast).client() diff --git a/app/helpers/pydantic_types/phone_numbers.py b/app/helpers/pydantic_types/phone_numbers.py index 670b3ff2..a96a0f2c 100644 --- a/app/helpers/pydantic_types/phone_numbers.py +++ b/app/helpers/pydantic_types/phone_numbers.py @@ -1,15 +1,16 @@ from datetime import tzinfo -from functools import lru_cache import phonenumbers from pydantic_extra_types.phone_numbers import PhoneNumber as PydanticPhoneNumber from pytz import country_timezones, timezone, utc +from app.helpers.cache import lru_cache + class PhoneNumber(PydanticPhoneNumber): phone_format = "E164" # E164 is standard accross all Microsoft services - @lru_cache # Cache results in memory as func is executed many times on the same content + @lru_cache() # Cache results in memory as func is executed many times on the same content def tz(self: PydanticPhoneNumber) -> tzinfo: """ Return timezone of a phone number. diff --git a/app/helpers/resources.py b/app/helpers/resources.py index 58bc0dc4..4cbbc5df 100644 --- a/app/helpers/resources.py +++ b/app/helpers/resources.py @@ -1,10 +1,11 @@ -from functools import lru_cache from os import getcwd from os.path import abspath, join from pathlib import Path +from app.helpers.cache import lru_cache -@lru_cache # Cache results in memory as resources are not expected to change + +@lru_cache() # Cache results in memory as resources are not expected to change def resources_dir(folder: str) -> str: """ Get the absolute path to the resources folder. diff --git a/app/helpers/translation.py b/app/helpers/translation.py index 9a3f33b8..d2a139ff 100644 --- a/app/helpers/translation.py +++ b/app/helpers/translation.py @@ -9,14 +9,14 @@ wait_random_exponential, ) -from app.helpers.cache import async_lru_cache +from app.helpers.cache import lru_acache from app.helpers.config import CONFIG from app.helpers.http import azure_transport from app.helpers.logging import logger logger.info("Using Translation %s", CONFIG.ai_translation.endpoint) -_cache = CONFIG.cache.instance() +_cache = CONFIG.cache.instance @retry( @@ -61,7 +61,7 @@ async def translate_text(text: str, source_lang: str, target_lang: str) -> str | return translation -@async_lru_cache() +@lru_acache() async def _use_client() -> TextTranslationClient: """ Generate the Translation client and close it after use. diff --git a/app/main.py b/app/main.py index 513fb50b..4a934bfb 100644 --- a/app/main.py +++ b/app/main.py @@ -41,7 +41,7 @@ from starlette.exceptions import HTTPException as StarletteHTTPException from twilio.twiml.messaging_response import MessagingResponse -from app.helpers.cache import async_lru_cache, get_scheduler +from app.helpers.cache import get_scheduler, lru_acache from app.helpers.call_events import ( on_audio_connected, on_automation_play_completed, @@ -106,14 +106,14 @@ ) # Persistences -_cache = CONFIG.cache.instance() -_call_queue = CONFIG.queue.call() -_db = CONFIG.database.instance() -_post_queue = CONFIG.queue.post() -_search = CONFIG.ai_search.instance() -_sms = CONFIG.sms.instance() -_sms_queue = CONFIG.queue.sms() -_training_queue = CONFIG.queue.training() +_cache = CONFIG.cache.instance +_call_queue = CONFIG.queue.call +_db = CONFIG.database.instance +_post_queue = CONFIG.queue.post +_search = CONFIG.ai_search.instance +_sms = CONFIG.sms.instance +_sms_queue = CONFIG.queue.sms +_training_queue = CONFIG.queue.training # Communication Services callback assert CONFIG.public_domain, "public_domain config is not set" @@ -1127,7 +1127,7 @@ def _standard_error( ) -@async_lru_cache() +@lru_acache() async def _use_automation_client() -> CallAutomationClient: """ Get the call automation client for Azure Communication Services. diff --git a/app/models/call.py b/app/models/call.py index c34c9945..9c3bc76d 100644 --- a/app/models/call.py +++ b/app/models/call.py @@ -130,7 +130,7 @@ async def trainings(self, cache_only: bool = True) -> list[TrainingModel]: from app.helpers.monitoring import tracer with tracer.start_as_current_span("call_trainings"): - search = CONFIG.ai_search.instance() + search = CONFIG.ai_search.instance tasks = await asyncio.gather( *[ search.training_search_all( diff --git a/app/persistence/ai_search.py b/app/persistence/ai_search.py index 5719c3c7..24ae49ae 100644 --- a/app/persistence/ai_search.py +++ b/app/persistence/ai_search.py @@ -45,7 +45,7 @@ wait_random_exponential, ) -from app.helpers.cache import async_lru_cache +from app.helpers.cache import lru_acache from app.helpers.config_models.ai_search import AiSearchModel from app.helpers.http import azure_transport from app.helpers.identity import credential @@ -182,7 +182,7 @@ async def training_search_all( return trainings or None - @async_lru_cache() + @lru_acache() async def _use_client(self) -> SearchClient: """ Get the search client. diff --git a/app/persistence/azure_queue_storage.py b/app/persistence/azure_queue_storage.py index df9f7f81..acac18ea 100644 --- a/app/persistence/azure_queue_storage.py +++ b/app/persistence/azure_queue_storage.py @@ -14,7 +14,7 @@ wait_random_exponential, ) -from app.helpers.cache import async_lru_cache, get_scheduler +from app.helpers.cache import get_scheduler, lru_acache from app.helpers.http import azure_transport from app.helpers.identity import credential from app.helpers.logging import logger @@ -158,7 +158,7 @@ async def _process_message( # Then, delete message await self.delete_message(message) - @async_lru_cache() + @lru_acache() async def _use_service_client(self) -> QueueServiceClient: """ Generate a new service client. diff --git a/app/persistence/communication_services.py b/app/persistence/communication_services.py index 4a3e6d47..520c705c 100644 --- a/app/persistence/communication_services.py +++ b/app/persistence/communication_services.py @@ -3,7 +3,7 @@ from azure.core.credentials import AzureKeyCredential from azure.core.exceptions import ClientAuthenticationError, HttpResponseError -from app.helpers.cache import async_lru_cache +from app.helpers.cache import lru_acache from app.helpers.config_models.communication_services import CommunicationServicesModel from app.helpers.http import azure_transport from app.helpers.logging import logger @@ -55,7 +55,7 @@ async def send(self, content: str, phone_number: PhoneNumber) -> bool: logger.exception("Error sending SMS to %s", phone_number) return success - @async_lru_cache() + @lru_acache() async def _use_client(self) -> SmsClient: logger.debug("Using SMS client for %s", self._config.endpoint) diff --git a/app/persistence/cosmos_db.py b/app/persistence/cosmos_db.py index 7b1de221..191854af 100644 --- a/app/persistence/cosmos_db.py +++ b/app/persistence/cosmos_db.py @@ -10,7 +10,7 @@ from azure.cosmos.exceptions import CosmosHttpResponseError, CosmosResourceNotFoundError from pydantic import ValidationError -from app.helpers.cache import async_lru_cache +from app.helpers.cache import lru_acache from app.helpers.config_models.database import CosmosDbModel from app.helpers.features import callback_timeout_hour from app.helpers.http import azure_transport @@ -373,7 +373,7 @@ async def _call_asearch_all_total_worker( return total - @async_lru_cache() + @lru_acache() async def _use_service_client(self) -> CosmosClient: """ Generate the Cosmos DB client. diff --git a/app/persistence/redis.py b/app/persistence/redis.py index feb35b72..776ea956 100644 --- a/app/persistence/redis.py +++ b/app/persistence/redis.py @@ -13,7 +13,7 @@ RedisError, ) -from app.helpers.cache import async_lru_cache +from app.helpers.cache import lru_acache from app.helpers.config_models.cache import RedisModel from app.helpers.logging import logger from app.models.readiness import ReadinessEnum @@ -116,7 +116,7 @@ async def delete(self, key: str) -> bool: return False return True - @async_lru_cache() + @lru_acache() async def _use_connection_pool(self) -> ConnectionPool: """ Generate the Redis connection pool. diff --git a/app/persistence/twilio.py b/app/persistence/twilio.py index b405d9cc..941392d3 100644 --- a/app/persistence/twilio.py +++ b/app/persistence/twilio.py @@ -1,7 +1,7 @@ from twilio.base.exceptions import TwilioRestException from twilio.rest import Client -from app.helpers.cache import async_lru_cache +from app.helpers.cache import lru_acache from app.helpers.config_models.sms import TwilioModel from app.helpers.http import twilio_http from app.helpers.logging import logger @@ -63,7 +63,7 @@ async def send(self, content: str, phone_number: PhoneNumber) -> bool: logger.exception("Error sending SMS to %s", phone_number) return success - @async_lru_cache() + @lru_acache() async def _use_client(self) -> Client: logger.debug("Using Twilio client for %s", self._config.account_sid) diff --git a/tests/cache.py b/tests/cache.py index d98dca76..5568f5e3 100644 --- a/tests/cache.py +++ b/tests/cache.py @@ -37,7 +37,7 @@ async def test_acid( """ # Set cache mode CONFIG.cache.mode = cache_mode - cache = CONFIG.cache.instance() + cache = CONFIG.cache.instance # Init values test_key = random_text diff --git a/tests/conftest.py b/tests/conftest.py index 8b23f981..ed645d2a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -308,7 +308,7 @@ def random_text() -> str: @pytest_asyncio.fixture async def call() -> CallStateModel: - db = CONFIG.database.instance() + db = CONFIG.database.instance call = await db.call_create( CallStateModel( initiate=CallInitiateModel( diff --git a/tests/llm.py b/tests/llm.py index 89644a21..39a41e3a 100644 --- a/tests/llm.py +++ b/tests/llm.py @@ -254,7 +254,7 @@ async def test_llm( # noqa: PLR0913 3. Test claim data exists 4. Test LLM metrics """ - db = CONFIG.database.instance() + db = CONFIG.database.instance def _play_media_callback(text: str) -> None: nonlocal actual_output diff --git a/tests/local.py b/tests/local.py index fac8a180..869efd4e 100644 --- a/tests/local.py +++ b/tests/local.py @@ -15,7 +15,7 @@ from app.models.message import MessageModel, PersonaEnum as MessagePersonaEnum from tests.conftest import CallAutomationClientMock, SpeechSynthesizerMock -_db = CONFIG.database.instance() +_db = CONFIG.database.instance async def main() -> None: diff --git a/tests/store.py b/tests/store.py index c74d844f..284e7461 100644 --- a/tests/store.py +++ b/tests/store.py @@ -20,7 +20,7 @@ async def test_acid(call: CallStateModel) -> None: Test is repeated 10 times to catch multi-threading and concurrency issues. """ - db = CONFIG.database.instance() + db = CONFIG.database.instance # Check not exists assume(not await db.call_get(call.call_id)) @@ -67,7 +67,7 @@ async def test_transaction( """ Test transactional properties of the database backend. """ - db = CONFIG.database.instance() + db = CONFIG.database.instance async with Scheduler() as scheduler: # Check not exists