
    {h&                    "   d Z ddlmZ ddlZddlZddlZddlmZmZ ddl	m
Z
 ddlmZ ddlZddlmZ g dZ G d	 d
e      Z G d de      Zd Z G d de      Zdd	 	 	 	 	 	 	 	 	 ddZ ej,                  d      ZddZ	 d	 	 	 	 	 	 	 	 	 ddZy)u  Caching utilities for Instructor.

This module provides a very small abstraction layer so that users can
plug different cache back-ends (in-process LRU, `diskcache`, `redis`, …)
into the Instructor client via the ``cache=...`` keyword::

    from instructor import from_provider
    from instructor.cache import AutoCache

    cache = AutoCache(maxsize=10_000)
    client = from_provider("openai/gpt-4o", cache=cache)

The cache object must implement :class:`BaseCache`.  A minimal
requirement is to expose synchronous ``get`` / ``set`` methods (async
wrappers currently call them directly).  The default implementation
``AutoCache`` is an in-process LRU cache with a configurable size.

This first iteration purposefully keeps the API narrow: no eviction
hooks, no invalidation, no TTL for the LRU variant.  The objective is to
provide a safe foundation which we will extend in follow-up work.
    )annotationsN)ABCabstractmethod)OrderedDict)Any)	BaseModel)	BaseCache	AutoCache	DiskCachemake_cache_keyc                  F    e Zd ZdZedd       Ze	 d	 	 	 	 	 	 	 dd       Zy)r	   zMAbstract cache contract.

    Concrete subclasses *must* be thread-safe.
    c                     y)z'Return *None* to indicate a cache miss.N selfkeys     Z/var/www/html/hubwallet-dev/venv/lib/python3.12/site-packages/instructor/cache/__init__.pygetzBaseCache.get3           Nc                     y)zStore *value* under *key*.

        ``ttl`` is time-to-live in **seconds**.  Implementations *may*
        ignore it (e.g. :class:`AutoCache`).
        Nr   r   r   valuettls       r   setzBaseCache.set7   r   r   r   strreturnz
Any | NoneNr   r   r   r   r   
int | Noner   None)__name__
__module____qualname____doc__r   r   r   r   r   r   r	   r	   -   sZ    
 6 6 
 	

 
 	

 

 
r   r	   c                  <    e Zd ZdZdddZddZ	 d		 	 	 	 	 	 	 d
dZy)r
   zHThread-safe in-process LRU cache using :class:`collections.OrderedDict`.c                    |dk  rt        d      || _        t               | _        t	        j
                         | _        y )Nr   zmaxsize must be > 0)
ValueError_maxsizer   _cache	threadingLock_lock)r   maxsizes     r   __init__zAutoCache.__init__H   s3    a<233-8]^^%
r   c                    | j                   5  	 | j                  j                  |      }|| j                  |<   |cd d d        S # t        $ r Y d d d        y w xY w# 1 sw Y   y xY wr   )r.   r+   popKeyError)r   r   r   s      r   r   zAutoCache.getR   sk    ZZ 	,  %DKK	 	  		 		 	s-   AAA	AAAAA$Nc                4   | j                   5  || j                  v r| j                  j                  |d        || j                  |<   t        | j                        | j                  kD  r| j                  j                  d       d d d        y # 1 sw Y   y xY w)NF)last)r.   r+   r2   lenr*   popitemr   s       r   r   zAutoCache.set\   sv     ZZ 	0dkk!T*$DKK4;;$--/###/	0 	0 	0s   A8BB)   )r/   intr   r   r    r#   r$   r%   r&   r0   r   r   r   r   r   r
   r
   E   s?    R& 	00 0 	0
 
0r   r
   c                 b    dd l } | j                  j                  d      t        d      dd l}|S )Nr   	diskcachezEdiskcache is not installed.  Install it with `pip install diskcache`.)	importlibutil	find_specImportErrorr<   )r=   r<   s     r   _import_diskcacherA   q   s5    ~~,4S
 	
 r   c                  ,    e Zd ZdZdddZddZd	d
dZy)r   z!Wrapper around `diskcache.Cache`.c                H    t               } |j                  |fi || _        y r   )rA   Cacher+   )r   	directorykwargsr<   s       r   r0   zDiskCache.__init__   s!    %'	%iooi:6:r   c                8    | j                   j                  |      S r   )r+   r   r   s     r   r   zDiskCache.get   s    {{s##r   Nc                ~    || j                   j                  ||       y | j                   j                  |||       y )N)expire)r+   r   r   s       r   r   zDiskCache.set   s/    ;KKOOC'KKOOCsO3r   )z.instructor_cache)rE   r   rF   r   r   r   r    r:   r   r   r   r   r   }   s    +;$4r   r   )modec                    || |d}||j                         |d<   t        j                  |dt              }t	        j
                  |j                               j                         S )u  Compute a *deterministic* cache key.

    The key space uses SHA-256("json payload") to keep the final length
    fixed regardless of input size.

    Components that influence the key:
        • provider/model name
        • serialized *messages* (user + system prompt, etc.)
        • *mode* (Tools, JSON, …) – helps when users change Instructor mode
        • *response_model* schema – so edits to field definitions or
          descriptions invalidate prior cache entries (critical!).
    )modelmessagesrJ   schemaT)	sort_keysdefault)model_json_schemajsondumpsr   hashlibsha256encode	hexdigest)rM   rL   response_modelrJ   payloaddatas         r   r   r      sc    * G ! +<<> ::gs;D>>$++-(2244r   zinstructor.cachec                  	
 | j                  |      }|yddl}	  |j                  |      }|d   }|j                  d      }|j	                  |      }|	 ddl} |j                  |      
t        
t              rLt        
fddD              r8ddlm		  |j                  |	fd	      |_
        t        j                  d
       n
|_
        t        j                  d       t        j                  d|       |S # t        $ r |}d}Y w xY w# |j                  t        f$ r ||_
        t        j                  d       Y ^w xY w)z9Return parsed model if *key* exists in *cache* else None.Nr   rL   rawc              3  &   K   | ]  }|v  
 y wr   r   ).0r   raw_datas     r   	<genexpr>z'load_cached_response.<locals>.<genexpr>   s      2$'x2s   )idobjectrL   choices)SimpleNamespacec                     di | S )Nr   r   )drd   s    r   <lambda>z&load_cached_response.<locals>.<lambda>   s    O4Ha4H r   )object_hookz/Restored raw response as SimpleNamespace objectz-Restored raw response as plain data structurezHRestored raw response as string (original could not be fully serialized)zcache hit: %s)r   rR   loads	Exceptionmodel_validate_json
isinstancedictanytypesrd   _raw_responseloggerdebugJSONDecodeError	TypeError)cacher   rX   cachedrR   rZ   
model_jsonraw_jsonobjrd   r_   s            @@r   load_cached_responserz      s=   YYs^F~tzz&!']
88E?
 
,
,Z
8C	!tzz(+H (D)c 2+O2 / 2$.DJJ*H%! NO %-!LM LL#&JI  
: $$i0 	 (CLLZ	s$   (C> BD >DD1EEc                   ddl }t        |dd      }|'	 |j                         }t        j	                  d       nd}|j                         |d}| j                  | |j                  |      |	       t        j	                  d
|       y# t
        t        f$ rr}	 ddl } |j                  |t              }t        j	                  d       n5# t        t        f$ r# t        |      }t        j                  d       Y nw xY wY d}~d}~ww xY w)zASerialize *model* and optional raw response to JSON and cache it.r   Nrp   z$Cached raw response as Pydantic JSON)rP   zPCached raw response as plain JSON (provider may not support full reconstruction)zRaw response could not be serialized as JSON, using string fallback. create_with_completion may not fully restore original object structure.)rL   r\   )r   zcache store: %s)rR   getattrmodel_dump_jsonrq   rr   AttributeErrorrt   rS   r   r)   warningr   )	ru   r   rL   r   rR   raw_resprx   erY   s	            r   store_cached_responser      s     uot4H	//1HLL?@$  &&(G 
IIc:4::g&CI0
LL"C(1 	* 	%4::h<f z* x=^	s5   %B D1CD/C?<D>C??DD)
rM   r   rL   
str | NonerX   ztype[BaseModel] | NonerJ   r   r   r   )ru   r	   r   r   rX   ztype[BaseModel]r   )
ru   r	   r   r   rL   r   r   r!   r   r"   )r&   
__future__r   rT   rR   r,   abcr   r   collectionsr   typingr   loggingpydanticr   __all__r	   r
   rA   r   r   	getLoggerrq   rz   r   r   r   r   <module>r      s   , #    # #    0$0	 $0X	4	 46 "5"5 "5 +	"5
 "5 	"5R 
		-	./f EI$)$)$)'0$)7A$)	$)r   