@@ -421,6 +421,84 @@ def json_safe(data: object) -> object:
421421 return data
422422
423423
424+ # Response Caching Classes
425+ class ResponseCache :
426+ """Simple in-memory response cache with TTL support."""
427+
428+ def __init__ (self , max_size : int = 100 , default_ttl : int = 300 ) -> None :
429+ """Initialize the cache.
430+
431+ Args:
432+ max_size: Maximum number of cached responses
433+ default_ttl: Default time-to-live in seconds
434+ """
435+ self .max_size : int = max_size
436+ self .default_ttl : int = default_ttl
437+ self ._cache : dict [str , tuple [Any , float ]] = {}
438+ self ._access_order : list [str ] = []
439+
440+ def _make_key (self , method : str , url : str , params : dict [str , Any ] | None = None , data : Any = None ) -> str :
441+ """Generate a cache key from request details."""
442+ import hashlib
443+ import json
444+
445+ key_data = {
446+ "method" : method .upper (),
447+ "url" : url ,
448+ "params" : params or {},
449+ "data" : json .dumps (data , sort_keys = True ) if data else None
450+ }
451+ key_str = json .dumps (key_data , sort_keys = True )
452+ return hashlib .md5 (key_str .encode ()).hexdigest ()
453+
454+ def get (self , method : str , url : str , params : dict [str , Any ] | None = None , data : Any = None ) -> Any | None :
455+ """Get a cached response if available and not expired."""
456+ import time
457+
458+ key = self ._make_key (method , url , params , data )
459+ if key in self ._cache :
460+ response , expiry = self ._cache [key ]
461+ if time .time () < expiry :
462+ # Move to end (most recently used)
463+ self ._access_order .remove (key )
464+ self ._access_order .append (key )
465+ return response
466+ else :
467+ # Expired, remove it
468+ del self ._cache [key ]
469+ self ._access_order .remove (key )
470+ return None
471+
472+ def set (self , method : str , url : str , response : Any , ttl : int | None = None ,
473+ params : dict [str , Any ] | None = None , data : Any = None ) -> None :
474+ """Cache a response with optional TTL."""
475+ import time
476+
477+ key = self ._make_key (method , url , params , data )
478+ expiry = time .time () + (ttl or self .default_ttl )
479+
480+ # Remove if already exists
481+ if key in self ._cache :
482+ self ._access_order .remove (key )
483+
484+ # Evict least recently used if at capacity
485+ if len (self ._cache ) >= self .max_size :
486+ lru_key = self ._access_order .pop (0 )
487+ del self ._cache [lru_key ]
488+
489+ self ._cache [key ] = (response , expiry )
490+ self ._access_order .append (key )
491+
492+ def clear (self ) -> None :
493+ """Clear all cached responses."""
494+ self ._cache .clear ()
495+ self ._access_order .clear ()
496+
497+ def size (self ) -> int :
498+ """Get current cache size."""
499+ return len (self ._cache )
500+
501+
424502# API Key Validation Functions
425503def validate_api_key (api_key : str | None ) -> bool :
426504 """Validate an API key format.
0 commit comments