o
    ?7i                     @   s   U d Z ddlZddlmZmZmZ ddlmZ ddlZe	e
ZzddlZdZed W n eyA   ddlZdZed Y nw G d	d
 d
Zdaee ed< defddZdS )z]
Query Result Caching Service

Provides Redis-based caching for frequently accessed queries.
    N)OptionalAnyCallablewrapsTz4Using orjson for high-performance JSON serializationFz2Using standard json library (orjson not available)c                   @   s   e Zd ZdZdd ZdededefddZd	edee	 fd
dZ
d!d	ede	defddZd	efddZdefddZd!dedefddZd"dedee fddZd"dedee fddZdefdd ZdS )#QueryCacheServicez*Service for caching query results in Redisc              
   C   s   z1ddl m} ddl}|jr|j|jdd| _n|j|j|j|j	|j
dd| _d| _td W dS  tyS } ztd| d	 d| _d
| _W Y d}~dS d}~ww )z.Initialize cache service with Redis connectionr   )settingsNT)decode_responses)hostportdbpasswordr	   z*Query cache service initialized with Redisz"Failed to initialize Redis cache: z. Caching disabled.F)
app.configr   redis	REDIS_URLfrom_urlredis_clientRedis
REDIS_HOST
REDIS_PORTREDIS_DBREDIS_PASSWORDenabledloggerinfo	Exceptionwarning)selfr   r   e r   F/lsinfo/ai/hellotax_ai/base_platform/app/services/cache/query_cache.py__init__   s   zQueryCacheService.__init__prefix	tenant_idreturnc              
   O   sZ   | d| dt | dt t|  }t|  dd }d| d| d| S )zBGenerate a cache key from function arguments with tenant isolation:N   cache:tenant:)strsorteditemshashlibsha256encode	hexdigest)r   r"   r#   argskwargskey_datakey_hashr   r   r    _generate_cache_key,   s   *z%QueryCacheService._generate_cache_keykeyc              
   C   s   | j sdS z#| j|}|r&tr t|tr|d}t|W S t	|W S W dS  t
yC } ztd|  W Y d}~dS d}~ww )zGet value from cacheNutf-8zCache get error: )r   r   get
USE_ORJSON
isinstancer(   r-   orjsonloadsjsonr   r   error)r   r4   valuer   r   r   r    r6   4   s    

zQueryCacheService.get,  r=   ttlc              
   C   s   | j sdS ztrtj|tdd}ntj|td}| j||| W dS  t	y? } zt
d|  W Y d}~dS d}~ww )z%Set value in cache with TTL (seconds)N)defaultr5   zCache set error: )r   r7   r9   dumpsr(   decoder;   r   setexr   r   r<   )r   r4   r=   r?   
serializedr   r   r   r    setH   s   zQueryCacheService.setc              
   C   sT   | j sdS z	| j| W dS  ty) } ztd|  W Y d}~dS d}~ww )zDelete value from cacheNzCache delete error: )r   r   deleter   r   r<   )r   r4   r   r   r   r    rF   X   s   zQueryCacheService.deletepatternc              
   C   sj   | j sdS z| j|}|r| jj|  W dS W dS  ty4 } ztd|  W Y d}~dS d}~ww )z Delete all keys matching patternNzCache delete pattern error: )r   r   keysrF   r   r   r<   )r   rG   rH   r   r   r   r    delete_patternb   s   z QueryCacheService.delete_patternc                    s   dt f fdd}|S )aW  
        Decorator for caching function results with tenant isolation

        Usage:
            @cache_service.cached("knowledge_bases", ttl=300)
            def get_knowledge_bases(db, tenant_id, user_id):
                return db.query(KnowledgeBase).all()

        Note: The decorated function must have tenant_id as a parameter
        funcc                    s   t   fdd}|S )Nc                     s   | d}|d u rt| dkr| D ]}t|dr|j} nq|d u r3td j d  | i |S j|g| R i |} |}|d urStd|  |S td|   | i |}	|| |S )Nr#   r   z'No tenant_id found for cached function z, skipping cachezCache hit: zCache miss: )
r6   lenhasattrr#   r   r   __name__r3   debugrE   )r/   r0   r#   arg	cache_keycached_valueresult)rJ   r"   r   r?   r   r    wrapperz   s&   


z<QueryCacheService.cached.<locals>.decorator.<locals>.wrapperr   )rJ   rS   r"   r   r?   )rJ   r    	decoratory   s   z+QueryCacheService.cached.<locals>.decorator)r   )r   r"   r?   rU   r   rT   r    cachedn   s   "zQueryCacheService.cachedNkb_idc                 C   D   |r|  d| d n	|  d| d td| d|  dS )z>Invalidate knowledge base related caches for a specific tenantr'   z:knowledge_bases:*z,Invalidated knowledge base cache for tenant=z, kb_id=NrI   r   r   )r   r#   rW   r   r   r    invalidate_knowledge_base_cache      z1QueryCacheService.invalidate_knowledge_base_cachecategory_idc                 C   rX   )z8Invalidate category related caches for a specific tenantr'   z:categories:*z&Invalidated category cache for tenant=z, category_id=NrY   )r   r#   r\   r   r   r    invalidate_category_cache   r[   z+QueryCacheService.invalidate_category_cachec                 C   s&   |  d| d td|  dS )z:Invalidate model configuration cache for a specific tenantr'   z:model_configs:*z1Invalidated model configuration cache for tenant=NrY   )r   r#   r   r   r    invalidate_model_config_cache   s   z/QueryCacheService.invalidate_model_config_cache)r>   )N)rM   
__module____qualname____doc__r!   r(   intr3   r   r   r6   rE   rF   rI   rV   rZ   r]   r^   r   r   r   r    r      s    
/r   _cache_servicer$   c                   C   s   t du rt a t S )z+Get or create query cache service singletonN)rc   r   r   r   r   r    get_query_cache_service   s   rd   )ra   loggingtypingr   r   r   	functoolsr   r+   	getLoggerrM   r   r9   r7   r   ImportErrorr;   r   rc   __annotations__rd   r   r   r   r    <module>   s&    
 