
    Q
if                        d Z ddlmZmZmZmZmZ ddlmZm	Z	 ddl
mZ ddlmZ ddlmZ ddlmZ ddlmZmZ  ee          Zd	eeef         d
eeef         fdZd	eeef         d
eeef         fdZ G d de          ZdS )zLangCache API-based LLM cache implementation for RedisVL.

This module provides an LLM cache implementation that uses the LangCache
managed service via the langcache Python SDK.
    )AnyDictListLiteralOptional)quoteunquote)BaseLLMCache)CacheHit)FilterExpression)
get_logger)denorm_cosine_distancenorm_cosine_distance
attributesreturnc                     | s| S d}t          |           }|                                 D ]8\  }}t          |t                    rt	          |d          }||k    r|||<   d}9|r|n| S )zReturn a copy of *attributes* with string values safely encoded.

    Only top-level string values are encoded; non-string values are left
    unchanged. If no values require encoding, the original dict is returned
    unchanged.
    F )safeT)dictitems
isinstancestrr   )r   changedsafe_attributeskeyvalueencodeds         C:\Users\Dell Inspiron 16\Desktop\tws\AgrotaPowerBi\back-agrota-powerbi\mcp-client-agrota\venv\Lib\site-packages\redisvl/extensions/cache/llm/langcache.py _encode_attributes_for_langcacher      s      G&*:&6&6O &&((  
UeS!! 	 E+++G%'.$%5??:5    c                     | s| S d}t          |           }|                                 D ]6\  }}t          |t                    rt	          |          }||k    r|||<   d}7|r|n| S )a+  Return a copy of *attributes* with string values safely decoded.

    This is the inverse of :func:`_encode_attributes_for_langcache`. Only
    top-level string values are decoded; non-string values are left unchanged.
    If no values require decoding, the original dict is returned unchanged.
    FT)r   r   r   r   r	   )r   r   decoded_attributesr   r   decodeds         r   !_decode_attributes_from_langcacher$   -   s      G)-j)9)9 &&((  
UeS!! 	ennG%*1"3'!(8j8r    c                       e Zd ZdZ	 	 	 	 	 	 	 	 d5ded	ed
ededee         dededed         f fdZ	d Z
dee         dee         fdZdedee         deeeef                  deeef         fdZdededeeeef                  fdZdeeef         defdZ	 	 	 	 	 	 	 d6dee         d eee                  ded!eee                  d"ee         dee         deeeef                  deeeef                  fd#Z	 	 	 	 	 	 	 d6dee         d eee                  ded!eee                  d"ee         dee         deeeef                  deeeef                  fd$Z	 	 	 	 d7deded eee                  d%eeeef                  d&eeeef                  dee         defd'Z	 	 	 	 d7deded eee                  d%eeeef                  d&eeeef                  dee         defd(Zd)eddfd*Zd)eddfd+Zd8d,Zd8d-Zd8d.Zd8d/Zd0eddfd1Zd0eddfd2Z deeef         deeef         fd3Z!deeef         deeef         fd4Z" xZ#S )9LangCacheSemanticCacheaX  LLM Cache implementation using the LangCache managed service.

    This cache uses the LangCache API service for semantic caching of LLM
    responses. It requires a LangCache account and API key.

    Example:
        .. code-block:: python

            from redisvl.extensions.cache.llm import LangCacheSemanticCache

            cache = LangCacheSemanticCache(
                name="my_cache",
                server_url="https://api.langcache.com",
                cache_id="your-cache-id",
                api_key="your-api-key",
                ttl=3600
            )

            # Store a response
            cache.store(
                prompt="What is the capital of France?",
                response="Paris"
            )

            # Check for cached responses
            results = cache.check(prompt="What is the capital of France?")
    	langcache(https://aws-us-east-1.langcache.redis.ior   NT
normalizedname
server_urlcache_idapi_keyttluse_exact_searchuse_semantic_searchdistance_scale)r)   redisc	                    |dvrt          d          || _        |st          d          |st          d           t                      j        d
||d|	 || _        || _        || _        g | _        |r| j                            d           |r| j                            d           | j        st          d          | 	                                | _
        d	S )u  Initialize a LangCache semantic cache.

        Args:
            name (str): The name of the cache. Defaults to "langcache".
            server_url (str): The LangCache server URL.
            cache_id (str): The LangCache cache ID.
            api_key (str): The LangCache API key.
            ttl (Optional[int]): Time-to-live for cache entries in seconds.
            use_exact_search (bool): Whether to use exact matching. Defaults to True.
            use_semantic_search (bool): Whether to use semantic search. Defaults to True.
            distance_scale (str): Threshold scale for distance_threshold:
                - "normalized": 0–1 semantic distance (lower is better)
                - "redis": Redis COSINE distance 0–2 (lower is better)

        Raises:
            ImportError: If the langcache package is not installed.
            ValueError: If cache_id or api_key is not provided.
        >   r2   r)   z.distance_scale must be 'normalized' or 'redis'z/cache_id is required for LangCacheSemanticCachez.api_key is required for LangCacheSemanticCache)r*   r.   exactsemanticzDAt least one of use_exact_search or use_semantic_search must be TrueN )
ValueError_distance_scalesuper__init___server_url	_cache_id_api_key_search_strategiesappend_create_client_client)selfr*   r+   r,   r-   r.   r/   r0   r1   kwargs	__class__s             r   r:   zLangCacheSemanticCache.__init__a   s   < !888MNNN- 	PNOOO 	OMNNN6d66v666%! #% 	4#**7333 	7#**:666& 	V   **,,r    c                     	 ddl m} n"# t          $ r}t          d          |d}~ww xY w || j        | j        | j                  S )z
        Initialize the LangCache client.

        Returns:
            LangCache: The LangCache client.

        Raises:
            ImportError: If the langcache package is not installed.
        r   )	LangCachezgThe langcache package is required to use LangCacheSemanticCache. Install it with: pip install langcacheN)r+   r,   r-   )r'   rF   ImportErrorr;   r<   r=   )rB   rF   es      r   r@   z%LangCacheSemanticCache._create_client   s    	+++++++ 	 	 	9  	 y'^M
 
 
 	
s   	 
(#(distance_thresholdr   c                 b    |dS | j         dk    rt          |          S dt          |          z
  S )u   Convert a distance threshold to a similarity threshold based on scale.

        - If distance_scale == "redis": use norm_cosine_distance (0–2 -> 0–1)
        - Otherwise: use (1.0 - distance_threshold) for normalized 0–1 distances
        Nr2         ?)r8   r   float)rB   rI   s     r   _similarity_thresholdz,LangCacheSemanticCache._similarity_threshold   s@     %47**'(:;;;U-....r    promptsimilarity_thresholdr   c                     ddl m} d| j        v r|j        nd d| j        v r|j        nd g}d |D             }|||d}|rt          |          |d<   |S )Nr   )SearchStrategyr4   r5   c                     g | ]}||S )Nr6   ).0ss     r   
<listcomp>z?LangCacheSemanticCache._build_search_kwargs.<locals>.<listcomp>   s    KKK1Q]Q]]]r    )rN   search_strategiesrO   r   )langcache.modelsrQ   r>   EXACTSEMANTICr   )rB   rN   rO   r   rQ   rV   rC   s          r   _build_search_kwargsz+LangCacheSemanticCache._build_search_kwargs   s     	433333
 %,t/F$F$FN  D'1T5L'L'LN##RV

 LK(9KKK!2$8"
 "

  	P $DJ#O#OF< r    responsenum_resultsc                 6   t          |d          r|j        ng }g }|d |         D ]r}t          |d          r|                                }nt          |          }|                     |          }|                    |                                           s|S )Ndata
model_dump)hasattrr^   r_   r   _convert_to_cache_hitr?   to_dict)rB   r[   r\   resultshitsresultresult_dicthits           r   _hits_from_responsez*LangCacheSemanticCache._hits_from_response   s     $+8V#<#<D(--"%'l{l+ 	' 	'Fv|,, +$//11"6ll,,[99CKK&&&&r    re   c                    |                     di           pi }|rt          |          }|                     dd          }| j        dk    rt          |          }nd|z
  }t	          |                     dd          |                     dd          |                     d	d          ||                     d
d          |                     dd          |r|nd          S )zConvert a LangCache result to a CacheHit object.

        Args:
            result (Dict[str, Any]): The result from LangCache.

        Returns:
            CacheHit: The converted cache hit.
        r   
similarityg        r2   rK   idr   rN   r[   
created_at
updated_atN)entry_idrN   r[   vector_distanceinserted_atrm   metadata)getr$   r8   r   r   )rB   re   r   rj   distances        r   ra   z,LangCacheSemanticCache._convert_to_cache_hit   s     ZZb117R
 	G ;:FFJ ZZc22
7**-j99HHZ'HZZb))::h++ZZ
B//$

<55zz,44#-7ZZ4
 
 
 	
r       vectorreturn_fieldsfilter_expressionc                 (   |st          d          |t                              d           |t                              d           d}||                     |          }|                     |||          }		  | j        j        d
i |	}
no# t          $ rb}	 ddlm	} n# t          $ r  w xY wt          ||          r5dt          |                                          v r|rt          d	          | d}~ww xY w|                     |
|          S )uS  Check the cache for semantically similar prompts.

        Args:
            prompt (Optional[str]): The text prompt to search for.
            vector (Optional[List[float]]): Not supported by LangCache API.
            num_results (int): Number of results to return. Defaults to 1.
            return_fields (Optional[List[str]]): Not used (for compatibility).
            filter_expression (Optional[FilterExpression]): Not supported.
            distance_threshold (Optional[float]): Maximum distance threshold.
                Converted to similarity_threshold according to distance_scale:
                  - If "redis": uses norm_cosine_distance(distance_threshold) ([0,2] → [0,1])
                  - If "normalized": uses (1.0 - distance_threshold) ([0,1] → [0,1])
            attributes (Optional[Dict[str, Any]]): LangCache attributes to filter by.
                Note: Attributes must be pre-configured in your LangCache instance.

        Returns:
            List[Dict[str, Any]]: List of matching cache entries.

        Raises:
            ValueError: If prompt is not provided.
        'prompt is required for LangCache searchN1LangCache does not support vector search directly-LangCache does not support filter expressionsrN   rO   r   r   BadRequestErrorResponseContentno attributes are configuredzLangCache reported attributes are not configured for this cache, but attributes were provided to check(). Remove attributes or configure them on the cache.r6   )r7   loggerwarningrM   rZ   rA   search	Exceptionlangcache.errorsr~   r   r   lowerRuntimeErrorrh   rB   rN   ru   r\   rv   rw   rI   r   rO   search_kwargsr[   rH   r~   s                r   checkzLangCacheSemanticCache.check  s   >  	HFGGGNNNOOO(NNJKKK  $)#'#=#=>P#Q#Q  11!5! 2 
 
	*t|*;;];;HH 	 	 	KKKKKKK    1<==
2c!ffllnnDD E #q  
 	$ ''+>>>s+   <B 
C;B! C6!B--A	C66C;c                 8  K   |st          d          |t                              d           |t                              d           d}||                     |          }|                     |||          }		  | j        j        d
i |	 d{V }
no# t          $ rb}	 ddlm	} n# t          $ r  w xY wt          ||          r5dt          |                                          v r|rt          d	          | d}~ww xY w|                     |
|          S )ad  Async check the cache for semantically similar prompts.

        Args:
            prompt (Optional[str]): The text prompt to search for.
            vector (Optional[List[float]]): Not supported by LangCache API.
            num_results (int): Number of results to return. Defaults to 1.
            return_fields (Optional[List[str]]): Not used (for compatibility).
            filter_expression (Optional[FilterExpression]): Not supported.
            distance_threshold (Optional[float]): Maximum distance threshold.
                Converted to similarity_threshold according to distance_scale:
                  - If "redis": uses norm_cosine_distance(distance_threshold) ([0,2] 												 -> [0,1])
                  - If "normalized": uses (1.0 - distance_threshold) ([0,1] -> [0,1])
            attributes (Optional[Dict[str, Any]]): LangCache attributes to filter by.
                Note: Attributes must be pre-configured in your LangCache instance.

        Returns:
            List[Dict[str, Any]]: List of matching cache entries.

        Raises:
            ValueError: If prompt is not provided.
        ry   Nrz   r{   r|   r   r}   r   zLangCache reported attributes are not configured for this cache, but attributes were provided to acheck(). Remove attributes or configure them on the cache.r6   )r7   r   r   rM   rZ   rA   search_asyncr   r   r~   r   r   r   r   rh   r   s                r   acheckzLangCacheSemanticCache.acheckW  s     >  	HFGGGNNNOOO(NNJKKK  $)#'#=#=>P#Q#Q  11!5! 2 
 
	6T\6GGGGGGGGGGHH 	 	 	KKKKKKK    1<==
2c!ffllnnDD E #r  
 	$ ''+>>>s+   >B 
D"B)(C>)B55A	C>>Drq   filtersc                    |st          d          |st          d          |t                              d           |t                              d           	 |t          |dz            nd}|r.t	          |          }| j                            ||||          }	n| j                            |||          }	no# t          $ rb}
	 d	d
lm	} n# t          $ r  w xY wt          |
|          r5dt          |
                                          v r|rt          d          |
 d}
~
ww xY wt          |	d          r|	j        ndS )ae  Store a prompt-response pair in the cache.

        Args:
            prompt (str): The user prompt to cache.
            response (str): The LLM response to cache.
            vector (Optional[List[float]]): Not supported by LangCache API.
            metadata (Optional[Dict[str, Any]]): Optional metadata (stored as attributes).
            filters (Optional[Dict[str, Any]]): Not supported.
            ttl (Optional[int]): Optional TTL override in seconds.

        Returns:
            str: The entry ID for the cached entry.

        Raises:
            ValueError: If prompt or response is empty.
        prompt is requiredresponse is requiredN)LangCache does not support custom vectors"LangCache does not support filters  rN   r[   r   
ttl_millisrN   r[   r   r   r}   r   zLangCache reported attributes are not configured for this cache, but metadata was provided to store(). Remove metadata or configure attributes on the cache.rn   r   )r7   r   r   roundr   rA   setr   r   r~   r   r   r   r   r`   rn   rB   rN   r[   ru   rq   r   r.   r   safe_metadatare   rH   r~   s               r   storezLangCacheSemanticCache.store  s   2  	31222 	53444NNFGGGNN?@@@	.1osTz***4J  @ J J))!%,)	 *   ))!%) *  
  	 	 	KKKKKKK    1<==
2c!ffllnnDD E #r  
 	& #*&*"="=Ev2Es,   A#C   
D,CD'CA	D''D,c                   K   |st          d          |st          d          |t                              d           |t                              d           	 |t          |dz            nd}|r4t	          |          }| j                            ||||           d{V }	n#| j                            |||           d{V }	no# t          $ rb}
	 d	d
lm	} n# t          $ r  w xY wt          |
|          r5dt          |
                                          v r|rt          d          |
 d}
~
ww xY wt          |	d          r|	j        ndS )ak  Async store a prompt-response pair in the cache.

        Args:
            prompt (str): The user prompt to cache.
            response (str): The LLM response to cache.
            vector (Optional[List[float]]): Not supported by LangCache API.
            metadata (Optional[Dict[str, Any]]): Optional metadata (stored as attributes).
            filters (Optional[Dict[str, Any]]): Not supported.
            ttl (Optional[int]): Optional TTL override in seconds.

        Returns:
            str: The entry ID for the cached entry.

        Raises:
            ValueError: If prompt or response is empty.
        r   r   Nr   r   r   r   r   r   r}   r   zLangCache reported attributes are not configured for this cache, but metadata was provided to astore(). Remove metadata or configure attributes on the cache.rn   r   )r7   r   r   r   r   rA   	set_asyncr   r   r~   r   r   r   r   r`   rn   r   s               r   astorezLangCacheSemanticCache.astore  s     2  	31222 	53444NNFGGGNN?@@@	.1osTz***4J  @ J J#|55!%,)	  6            $|55!%)  6          
  	 	 	KKKKKKK    1<==
2c!ffllnnDD E #s  
 	& #*&*"="=Ev2Es,   A/C 
D:C D5 C,,A	D55D:r   c                      t          d          )a  Update specific fields within an existing cache entry.

        Note: LangCache API does not support updating individual entries.
        This method will raise NotImplementedError.

        Args:
            key (str): The key of the document to update.
            **kwargs: Field-value pairs to update.

        Raises:
            NotImplementedError: LangCache does not support entry updates.
        cLangCache API does not support updating individual entries. Delete and re-create the entry instead.NotImplementedErrorrB   r   rC   s      r   updatezLangCacheSemanticCache.update6  s     "6
 
 	
r    c                 $   K   t          d          )a  Async update specific fields within an existing cache entry.

        Note: LangCache API does not support updating individual entries.
        This method will raise NotImplementedError.

        Args:
            key (str): The key of the document to update.
            **kwargs: Field-value pairs to update.

        Raises:
            NotImplementedError: LangCache does not support entry updates.
        r   r   r   s      r   aupdatezLangCacheSemanticCache.aupdateH  s       "6
 
 	
r    c                 8    | j                                          dS )zjDelete the entire cache.

        This deletes all entries in the cache by calling the flush API.
        N)rA   flushrB   s    r   deletezLangCacheSemanticCache.deleteZ  s    
 	r    c                 H   K   | j                                          d{V  dS )zpAsync delete the entire cache.

        This deletes all entries in the cache by calling the flush API.
        N)rA   flush_asyncr   s    r   adeletezLangCacheSemanticCache.adeletea  s4      
 l&&(((((((((((r    c                 .    |                                   dS )zqClear the cache of all entries.

        This is an alias for delete() to match the BaseCache interface.
        N)r   r   s    r   clearzLangCacheSemanticCache.clearh  s    
 	r    c                 >   K   |                                   d{V  dS )zxAsync clear the cache of all entries.

        This is an alias for adelete() to match the BaseCache interface.
        N)r   r   s    r   aclearzLangCacheSemanticCache.aclearo  s.      
 llnnr    rn   c                 <    | j                             |           dS )zuDelete a single cache entry by ID.

        Args:
            entry_id (str): The ID of the entry to delete.
        rn   N)rA   delete_by_idrB   rn   s     r   r   z#LangCacheSemanticCache.delete_by_idv  s#     	!!8!44444r    c                 L   K   | j                             |           d{V  dS )z{Async delete a single cache entry by ID.

        Args:
            entry_id (str): The ID of the entry to delete.
        r   N)rA   delete_by_id_asyncr   s     r   adelete_by_idz$LangCacheSemanticCache.adelete_by_id~  s9       l--x-@@@@@@@@@@@r    c                     |st          d          t          |          }| j                            |          }t	          |d          r|                                ni S )aV  Delete cache entries matching the given attributes.

        Args:
            attributes (Dict[str, Any]): Attributes to match for deletion.
                Cannot be empty.

        Returns:
            Dict[str, Any]: Result of the deletion operation.

        Raises:
            ValueError: If attributes is an empty dictionary.
        @Cannot delete by attributes with an empty attributes dictionary.r   r_   )r7   r   rA   delete_queryr`   r_   rB   r   r   re   s       r   delete_by_attributesz+LangCacheSemanticCache.delete_by_attributes  sn      	R   ;:FF**o*FF&-fl&C&CKv  """Kr    c                    K   |st          d          t          |          }| j                            |           d{V }t	          |d          r|                                ni S )a\  Async delete cache entries matching the given attributes.

        Args:
            attributes (Dict[str, Any]): Attributes to match for deletion.
                Cannot be empty.

        Returns:
            Dict[str, Any]: Result of the deletion operation.

        Raises:
            ValueError: If attributes is an empty dictionary.
        r   r   Nr_   )r7   r   rA   delete_query_asyncr`   r_   r   s       r   adelete_by_attributesz,LangCacheSemanticCache.adelete_by_attributes  s        	R   ;:FF|66/6RRRRRRRR&-fl&C&CKv  """Kr    )r'   r(   r   r   NTTr)   )NNrt   NNNN)NNNN)r   N)$__name__
__module____qualname____doc__r   r   intboolr   r:   r@   rL   rM   r   r   rZ   r   rh   r   ra   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   __classcell__)rD   s   @r   r&   r&   D   s        <  D!!%$(9E9- 9-9- 9- 	9-
 9- c]9- 9- "9-   569- 9- 9- 9- 9- 9-v
 
 
0/"*5//	%/ / / / 'uo T#s(^,	
 
c3h   6*-	d38n	    
DcN  
x  
  
  
  
H !%(,-18<.2/3H? H?H? e%H? 	H?
  S	*H? $$45H? %UOH? T#s(^,H? 
d38n	H? H? H? H?X !%(,-18<.2/3K? K?K? e%K? 	K?
  S	*K? $$45K? %UOK? T#s(^,K? 
d38n	K? K? K? K?b )--1,0!GF GFGF GF e%	GF
 4S>*GF $sCx.)GF c]GF 
GF GF GF GFZ )--1,0!GF GFGF GF e%	GF
 4S>*GF $sCx.)GF c]GF 
GF GF GF GFR
# 
D 
 
 
 
$
 
4 
 
 
 
$   ) ) ) )      5S 5T 5 5 5 5AC AD A A A ALtCH~ L$sCx. L L L L,Ld38n LcSVh L L L L L L L Lr    r&   N)r   typingr   r   r   r   r   urllib.parser   r	   !redisvl.extensions.cache.llm.baser
   #redisvl.extensions.cache.llm.schemar   redisvl.query.filterr   redisvl.utils.logr   redisvl.utils.utilsr   r   r   r   r   r   r$   r&   r6   r    r   <module>r      sn    6 5 5 5 5 5 5 5 5 5 5 5 5 5 ' ' ' ' ' ' ' ' : : : : : : 8 8 8 8 8 8 1 1 1 1 1 1 ( ( ( ( ( ( L L L L L L L L	H		6c3h 6DcN 6 6 6 649$sCx. 9T#s(^ 9 9 9 9.l	L l	L l	L l	L l	L\ l	L l	L l	L l	L l	Lr    