
    =Kib                       S r SSKJr  SSKrSSKrSSKrSSKrSSKrSSKrSSK	J
r
  SSKJr  SSKJr  SSKJr  SSKJr  SS	KJrJrJrJrJr  \(       a   \R2                  " S
5      rSrSrSr\ " S S5      5       r\ " S S5      5       r " S S\
5      r  " S S\ 5      r! " S S\ 5      r"\!" 5       r#\"" 5       r$\\\S.       S S jjr%\\\S.       S S jjr&S!S jr' " S S\!5      r( " S S\"5      r)g)"zPrompt caching module for LangSmith SDK.

This module provides thread-safe LRU caches with background refresh
for prompt caching. Includes both sync and async implementations.
    )annotationsN)ABC)OrderedDict)	Awaitable)	dataclass)Path)TYPE_CHECKINGAnyCallableOptionalUnionzlangsmith.cachei,  d   <   c                  F    \ rS rSr% SrS\S'   S\S'   SrS\S	'   SS
 jrSrg)
CacheEntry!   z4A single cache entry with metadata for TTL tracking.r
   valuefloat
created_atNzOptional[Callable[[], Any]]refresh_funcc                T    Uc  g[         R                   " 5       U R                  -
  U:  $ )z/Check if entry is past its TTL (needs refresh).F)timer   )selfttl_secondss     X/var/www/html/dynamic-report/venv/lib/python3.13/site-packages/langsmith/prompt_cache.pyis_staleCacheEntry.is_stale)   s%    		doo-<<     )r   Optional[float]returnbool)	__name__
__module____qualname____firstlineno____doc____annotations__r   r   __static_attributes__r   r   r   r   r   !   s     >J04L-4=r   r   c                  z    \ rS rSr% SrSrS\S'   SrS\S'   SrS\S'   Sr	S\S'   \
SS	 j5       r\
SS
 j5       rSrg)CacheMetrics0   zCache performance metrics.r   inthitsmisses	refreshesrefresh_errorsc                4    U R                   U R                  -   $ )z%Total cache requests (hits + misses).)r.   r/   r   s    r   total_requestsCacheMetrics.total_requests9   s     yy4;;&&r   c                H    U R                   nUS:  a  U R                  U-  $ S$ )zCache hit rate (0.0 to 1.0).r   g        )r4   r.   )r   totals     r   hit_rateCacheMetrics.hit_rate>   s)     ##$)AItyy5 636r   r   N)r!   r-   )r!   r   )r#   r$   r%   r&   r'   r.   r(   r/   r0   r1   propertyr4   r8   r)   r   r   r   r+   r+   0   sQ    $D#MFCOIsNC' ' 7 7r   r+   c                      \ rS rSrSr/ SQr\\\4       SS jjr	\
SS j5       rSS jrSS jrSS jrSS	 jrSS
 jrSS jrSS jrSS jr        SS jrSrg)_BasePromptCacheE   zBase class for prompt caches with shared LRU logic.

Provides thread-safe in-memory LRU cache operations.
Subclasses implement the background refresh mechanism.
)_cache_lock	_max_size_ttl_seconds_refresh_interval_metricsc                    [        5       U l        [        R                  " 5       U l        [        5       U l        U R                  UUUS9  g)a7  Initialize the base cache.

Args:
    max_size: Maximum entries in cache (LRU eviction when exceeded).
    ttl_seconds: Time before entry is considered stale. Set to None for
        infinite TTL (entries never expire, no background refresh).
    refresh_interval_seconds: How often to check for stale entries.
max_sizer   refresh_interval_secondsN)r   r>   	threadingRLockr?   r+   rC   
_configurer   rF   r   rG   s       r   __init___BasePromptCache.__init__U   s?     5@M__&
$#%= 	 	
r   c                    U R                   $ )zGet cache performance metrics.)rC   r3   s    r   metrics_BasePromptCache.metricsl   s     }}r   c                "    [        5       U l        g)zReset all metrics to zero.N)r+   rC   r3   s    r   reset_metrics_BasePromptCache.reset_metricsq   s    $r   c                   U R                   S:X  a  gU R                     XR                  ;  a)  U R                  =R                  S-  sl         SSS5        gU R                  U   nX#l        U R                  R                  U5        U R                  =R                  S-  sl        UR                  sSSS5        $ ! , (       d  f       g= f)a%  Get a value from cache.

Args:
    key: The cache key (prompt identifier like "owner/name:hash").
    refresh_func: Function to refresh this cache entry when stale.

Returns:
    The cached value or None if not found.
    Stale entries are still returned (background refresh handles updates).
r   N   )	r@   r?   r>   rC   r/   r   move_to_endr.   r   )r   keyr   entrys       r   get_BasePromptCache.getu   s     >>QZZ++%$$)$ Z
 KK$E ". KK##C(MM!#;; ZZs   0B;AB;;
C	c                   U R                   S:X  a  gU R                     [        R                  " 5       n[        X$US9nXR                  ;  at  [        U R                  5      U R                   :  aQ  [        [        U R                  5      5      nU R                  R                  U5        [        R                  SU 35        XPR                  U'   U R                  R                  U5        SSS5        g! , (       d  f       g= f)Set a value in the cache.

Args:
    key: The cache key (prompt identifier).
    value: The value to cache.
    refresh_func: Function to refresh this cache entry when stale.
r   N)r   r   r   zEvicted oldest cache entry: )r@   r?   r   r   r>   lennextiterpoploggerdebugrV   )r   rW   r   r   nowrX   
oldest_keys          r   _set_BasePromptCache._set   s     >>QZZ))+CUVE ++%#dkk*:dnn*L!$t{{"34

+;J<HI$KKKK##C( ZZs   CC33
Dc                    U R                      U R                  R                  US5        SSS5        g! , (       d  f       g= f)zQRemove a specific entry from cache.

Args:
    key: The cache key to invalidate.
N)r?   r>   r`   )r   rW   s     r   
invalidate_BasePromptCache.invalidate   s'     ZZKKOOC& ZZs	   3
Ac                    U R                      U R                  R                  5         SSS5        g! , (       d  f       g= f)z$Clear all cache entries from memory.N)r?   r>   clearr3   s    r   rk   _BasePromptCache.clear   s#    ZZKK ZZs   1
?c                    U R                      U R                  R                  5        VVs/ s H*  u  pUR                  U R                  5      (       d  M'  X4PM,     snnsSSS5        $ s  snnf ! , (       d  f       g= f)z.Get list of stale cache entries (thread-safe).N)r?   r>   itemsr   rA   )r   rW   rX   s      r   _get_stale_entries#_BasePromptCache._get_stale_entries   s_    ZZ #'++"3"3"5"5JC>>$"3"34 "5 Z Zs"   A/&A)A)A/)A//
A=c                z   SSK Jn  [        U5      nUR                  R	                  SSS9  U R
                     0 nU R                  R                  5        H  u  pE[        UR                  UR                  5      (       aP  [        UR                  S5      (       a  UR                  R                  SS9nO'UR                  R                  5       nOUR                  nXcU'   M     SU0nS	S	S	5        UR                  S
5      n [        US5       n	[         R"                  " WU	SS9  S	S	S	5        UR%                  U5        [&        R)                  S[+        W5       SU 35        g	! , (       d  f       N= f! , (       d  f       NU= f! [,         a,  n
UR/                  5       (       a  UR1                  5         U
eS	n
A
ff = f)zcDump cache contents to a JSON file for offline use.

Args:
    path: Path to the output JSON file.
r   schemasT)parentsexist_ok
model_dumpjson)modeentriesNz.tmpw   )indentzDumped z cache entries to )	langsmithrs   r   parentmkdirr?   r>   rn   
isinstancer   PromptCommithasattrrv   dictwith_suffixopenrw   dumpreplacera   rb   r]   	Exceptionexistsunlink)r   path
ls_schemasry   rW   rX   
value_datadata	temp_pathfes              r   r   _BasePromptCache.dump   sf    	4Dz$6ZZG"kk//1
ekk:+B+BCCu{{L99%*[[%;%;%;%H
%*[[%5%5%7
 "'J) 2 w'D! & $$V,			i%		$!, &d#LL73w<.0B4&IJ1 Z* &%  	!!  "G		s<   B/E"F E3$=F "
E03
F=F 
F:'F55F:c           	     H   SSK Jn  [        U5      nUR                  5       (       d  [        R                  SU 35        g [        U5       n[        R                  " U5      nSSS5        WR                  S0 5      nSn[        R                  " 5       nU R                     UR                  5        H  u  p[!        U R"                  5      U R$                  :  a  [        R                  SU 35          Os ['        UR(                  S	5      (       a  UR(                  R+                  U
5      nOUR(                  R-                  U
5      n[/        XS
9nXR"                  U	'   US-  nM     SSS5        [        R                  SU SU 35        U$ ! , (       d  f       GN/= f! [        R                  [        4 a%  n[        R                  SU SU 35         SnAgSnAff = f! [0         a'  n[        R                  SU	 SU 35         SnAGMZ  SnAff = f! , (       d  f       N= f)zLoad cache contents from a JSON file.

Args:
    path: Path to the JSON file to load.

Returns:
    Number of entries loaded.

Loaded entries get a fresh TTL starting from load time.
If the file doesn't exist or is corrupted, returns 0.
r   rr   zCache file not found: NzFailed to load cache file : ry   z)Reached max cache size, stopping load at model_validate)r   r   rU   zFailed to load cache entry zLoaded z cache entries from )r}   rs   r   r   ra   rb   r   rw   loadJSONDecodeErrorOSErrorwarningrY   r   r?   rn   r]   r>   r@   r   r   r   	parse_objr   r   )r   r   r   r   r   r   ry   loadedrc   rW   r   r   rX   s                r   r   _BasePromptCache.load   s    	4Dz{{}}LL1$89	dqyy|  ((9b)iikZZ#*==?t{{#t~~5LL#LVH!UVz668HII * 7 7 F Fz R * 7 7 A A* M 'UCE',KK$aKF $3 * 	wvh&:4&ABA $$g. 	NN7vRsCD	2 ! NN%@Rs#KL# Zsn   F F#F  AH4A.G"H
FF F G7GG
H)HHHH
H!c                (    Xl         X l        X0l        g )N)r@   rA   rB   rK   s       r   rJ   _BasePromptCache._configure&  s     "'!9r   )r>   r?   r@   rC   rB   rA   NrF   r-   r   r    rG   r   r!   None)r!   r+   r!   r   )rW   strr   Callable[[], Any]r!   zOptional[Any]rW   r   r   r
   r   r   r!   r   )rW   r   r!   r   )r!   zlist[tuple[str, CacheEntry]])r   Union[str, Path]r!   r   )r   r   r!   r-   )r#   r$   r%   r&   r'   	__slots__DEFAULT_PROMPT_CACHE_MAX_SIZE DEFAULT_PROMPT_CACHE_TTL_SECONDS-DEFAULT_PROMPT_CACHE_REFRESH_INTERVAL_SECONDSrL   r:   rO   rR   rY   re   rh   rk   ro   r   r   rJ   r)   r   r   r   r<   r<   E   s    I 6'G*W	

 %
 #(	

 

.  '>)4' 
(T5n:: %: #(	:
 
:r   r<   c                     ^  \ rS rSrSrSS/r\\\S.       SU 4S jjjr	SS jr
SS jrSS	 jrSS
 jrSS jrSS jr\\\S.       SS jjrSrU =r$ )PromptCachei1  a  Thread-safe LRU cache with background thread refresh.

For use with the synchronous Client.

Features:
- In-memory LRU cache with configurable max size
- Background thread for refreshing stale entries
- Stale-while-revalidate: returns stale data while refresh happens
- Thread-safe for concurrent access

Example:
    >>> def fetch_prompt(key: str) -> PromptCommit:
    ...     return client._fetch_prompt_from_api(key)
    >>> cache = PromptCache(
    ...     max_size=100,
    ...     ttl_seconds=3600,
    ...     fetch_func=fetch_prompt,
    ... )
    >>> cache.set("my-prompt:latest", prompt_commit)
    >>> cached = cache.get("my-prompt:latest")
    >>> cache.shutdown()
_shutdown_event_refresh_threadrE   c               f   > [         TU ]  UUUS9  [        R                  " 5       U l        SU l        g)aX  Initialize the sync prompt cache.

Args:
    max_size: Maximum entries in cache (LRU eviction when exceeded).
    ttl_seconds: Time before entry is considered stale. Set to None for
        infinite TTL (offline mode - entries never expire).
        Default: 300 (5 minutes).
    refresh_interval_seconds: How often to check for stale entries.
rE   N)superrL   rH   Eventr   r   r   rF   r   rG   	__class__s       r   rL   PromptCache.__init__K  s9      	#%= 	 	

  )0;?r   c                b    U R                   c  U R                  5         U R                  XU5        g)r\   N)r   _start_refresh_threadre   r   rW   r   r   s       r   setPromptCache.sete  s*     '&&(		#l+r   c                $    U R                  5         g)WStop background refresh thread.

Should be called when the client is being cleaned up.
N)shutdownr3   s    r   stopPromptCache.stopr  s    
 	r   c                    U R                   b  U R                   R                  5         U R                  b!  U R                  R                  SS9  SU l        gg)r   Ng      @)timeout)r   r   r   joinr3   s    r   r   PromptCache.shutdowny  sR    
 +  $$&+  %%c%2#'D  ,r   c                    U R                   bo  U R                  R                  5         [        R                  " U R
                  SSS9U l        U R                  R                  5         [        R                  S5        gg)z5Start background thread for refreshing stale entries.NTzPromptCache-refresh)targetdaemonnamezStarted cache refresh thread)
rA   r   rk   rH   Thread_refresh_loopr   startra   rb   r3   s    r   r   !PromptCache._start_refresh_thread  sh    (  &&(#,#3#3))*$D 
   &&(LL78 )r   c                2   U R                   R                  U R                  5      (       d>   U R                  5         U R                   R                  U R                  5      (       d  M=  gg! [         a"  n[
        R                  SU 35         SnANUSnAff = f)z)Background loop to refresh stale entries.z(Unexpected error in cache refresh loop: N)r   waitrB   _refresh_stale_entriesr   ra   	exceptionr   r   s     r   r   PromptCache._refresh_loop  s}    &&++D,B,BCCQ++- &&++D,B,BCC  Q  #KA3!OPPQs   A* *
B4BBc                Z   U R                  5       nU(       d  g[        R                  S[        U5       S35        U H  u  p#U R                  R                  5       (       a    gUR                  c  M5   UR                  5       nU R                  X$UR                  5        U R                  =R                  S-  sl	        [        R                  SU 35        M     g! [         aE  nU R                  =R                  S-  sl        [        R                  SU SU 35         SnAM  SnAff = f)z)Check for stale entries and refresh them.NzRefreshing  stale cache entriesrU   zRefreshed cache entry: zFailed to refresh cache entry r   )ro   ra   rb   r]   r   is_setr   r   rC   r0   r   r1   r   r   stale_entriesrW   rX   	new_valuer   s         r   r   "PromptCache._refresh_stale_entries  s    //1{3}#5"66JKL'JC##**,,!!-P % 2 2 4IHHSU-?-?@MM++q0+LL#:3%!@A ( ! PMM00A50NN%CC51##NOOPs   3A#C
D*%:D%%D*c               F    U R                  5         U R                  UUUS9  g)Reconfigure the cache parameters.

Args:
    max_size: Maximum entries in cache (LRU eviction when exceeded).
    ttl_seconds: Time before entry is considered stale.
    refresh_interval_seconds: How often to check for stale entries.
rE   Nr   rJ   rK   s       r   	configurePromptCache.configure  s'     			#%= 	 	
r   )r   r   r   r   r   )r#   r$   r%   r&   r'   r   r   r   r   rL   r   r   r   r   r   r   r   r)   __classcell__r   s   @r   r   r   1  s    . #$56I
 6'G*W@ @ %	@
 #(@ 
@ @4,	(
9QP4 6'G*W
 
 %	

 #(
 

 
r   r   c                     ^  \ rS rSrSrS/r\\\S.       SU 4S jjjr	        SS jr
SS jrSS jrSS	 jrSS
 jrSS jr\\\S.       SS jjrSrU =r$ )AsyncPromptCachei  a  Thread-safe LRU cache with asyncio task refresh.

For use with the asynchronous AsyncClient.

Features:
- In-memory LRU cache with configurable max size
- Asyncio task for refreshing stale entries
- Stale-while-revalidate: returns stale data while refresh happens
- Thread-safe for concurrent access

Example:
    >>> async def fetch_prompt(key: str) -> PromptCommit:
    ...     return await client._afetch_prompt_from_api(key)
    >>> cache = AsyncPromptCache(
    ...     max_size=100,
    ...     ttl_seconds=3600,
    ...     fetch_func=fetch_prompt,
    ... )
    >>> await cache.start()
    >>> cache.set("my-prompt:latest", prompt_commit)
    >>> cached = cache.get("my-prompt:latest")
    >>> await cache.stop()
_refresh_taskrE   c               2   > [         TU ]  UUUS9  SU l        g)a7  Initialize the async prompt cache.

Args:
    max_size: Maximum entries in cache (LRU eviction when exceeded).
    ttl_seconds: Time before entry is considered stale. Set to None for
        infinite TTL (offline mode - entries never expire).
    refresh_interval_seconds: How often to check for stale entries.
rE   N)r   rL   r   r   s       r   rL   AsyncPromptCache.__init__  s+     	#%= 	 	

 <@r   c                ~   #    U R                   c  U R                  5       I Sh  vN   U R                  XU5        g N7f)zSet a value in the cache.

Args:
    key: The cache key (prompt identifier).
    value: The value to cache.
    refresh_func: Async function to refresh this cache entry when stale.
N)r   r   re   r   s       r   asetAsyncPromptCache.aset  s5      %**,		#l+ s   !=;=c                   #    U R                   c  gU R                  b  g[        R                  " U R	                  5       SS9U l        [
        R                  S5        g7f)zStart async background refresh loop.

Must be called from an async context. Creates an asyncio task that
periodically checks for stale entries and refreshes them.
Does nothing if ttl_seconds is None (infinite TTL mode).
NzAsyncPromptCache-refresh)r   z Started async cache refresh task)rA   r   asynciocreate_taskr   ra   rb   r3   s    r   r   AsyncPromptCache.start  sV      $)$00 +
 	78s   AAc                b    U R                   b"  U R                   R                  5         SU l         gg)zStop background refresh task.

Synchronous wrapper that cancels the refresh task.
For proper cleanup in async context, use stop() instead.
N)r   cancelr3   s    r   r   AsyncPromptCache.shutdown  s/     )%%'!%D *r   c                   #    U R                   c  gU R                   R                  5          U R                   I Sh  vN   SU l         [        R                  S5        g N!! [        R                   a     N5f = f7f)z\Stop async background refresh loop.

Cancels the refresh task and waits for it to complete.
Nz Stopped async cache refresh task)r   r   r   CancelledErrorra   rb   r3   s    r   r   AsyncPromptCache.stop$  so     
 %!!#	$$$$ "78	 %%% 		s8   )A9A AA  A9A A63A95A66A9c                   #      [         R                  " U R                  5      I Sh  vN   U R                  5       I Sh  vN   MC   N N! [         R                   a    e [
         a"  n[        R                  SU 35         SnANASnAff = f7f)z/Async background loop to refresh stale entries.Nz.Unexpected error in async cache refresh loop: )r   sleeprB   r   r   r   ra   r   r   s     r   r   AsyncPromptCache._refresh_loop4  s     WmmD$:$:;;;11333 ;3))  W  #QRSQT!UVVWsP   B#A AA  A	A BA 	A B)BBBBc                H  #    U R                  5       nU(       d  g[        R                  S[        U5       S35        U H  u  p#UR                  c  M   UR	                  5       I Sh  vN nU R                  X$UR                  5      I Sh  vN   U R                  =R                  S-  sl        [        R                  SU 35        M     g Nd NB! [         aE  nU R                  =R                  S-  sl	        [        R                  SU SU 35         SnAM  SnAff = f7f)z8Check for stale entries and refresh them asynchronously.NzAsync refreshing r   rU   zAsync refreshed cache entry: z$Failed to async refresh cache entry r   )ro   ra   rb   r]   r   r   rC   r0   r   r1   r   r   s         r   r   'AsyncPromptCache._refresh_stale_entries@  s     //1(]);(<<PQR'JC!!-V&+&8&8&: :I))CE4F4FGGGMM++q0+LL#@!FG ( !;G ! VMM00A50NN%I#bQRPS#TUUVsT   AD"C'C(#CC;CD"CC
D:DD"DD"c               d   #    U R                  5       I Sh  vN   U R                  XU5        g N7f)r   Nr   rK   s       r   r   AsyncPromptCache.configureU  s*      iik/GH 	s   0.0)r   r   )rW   r   r   r
   r   zCallable[[], Awaitable[Any]]r!   r   r   )r#   r$   r%   r&   r'   r   r   r   r   rL   r   r   r   r   r   r   r   r)   r   r   s   @r   r   r     s    0 !!I
 6'G*W@ @ %	@
 #(@ 
@ @,,,",2N,	,9(&9 
WV0 6'G*WI I %	I
 #(I 
I Ir   r   rE   c                .    [         R                  U UUS9  ga  Configure the global prompt cache.

This should be called before any cache instances are created or used.

Args:
    max_size: Maximum entries in cache (LRU eviction when exceeded).
    ttl_seconds: Time before entry is considered stale.
    refresh_interval_seconds: How often to check for stale entries.

Example:
    >>> from langsmith import configure_global_prompt_cache
    >>> configure_global_prompt_cache(max_size=200, ttl_seconds=7200)
rE   N)prompt_cache_singletonr   rE   s      r   configure_global_prompt_cacher   l  s     & $$!9 % r   c                J   #    [         R                  U UUS9I Sh  vN   g N7fr   )async_prompt_cache_singletonr   rE   s      r   #configure_global_async_prompt_cacher     s-     & '
0
0!9 1   s   #!#c                 8    [         R                  " S[        SS9  g )NzcThe 'Cache' class is deprecated and will be removed in a future version. Use 'PromptCache' instead.   )
stacklevel)warningswarnDeprecationWarningr   r   r   _deprecated_cache_class_warningr    s    MM	%	r   c                  J   ^  \ rS rSrSr\\\S.       SU 4S jjjrSr	U =r
$ )Cachei  z:Deprecated alias for PromptCache. Use PromptCache instead.rE   c               8   > [        5         [        TU ]	  UUUS9  g)zInitialize the deprecated Cache class.

Args:
    max_size: Maximum entries in cache (LRU eviction when exceeded).
    ttl_seconds: Time before entry is considered stale.
    refresh_interval_seconds: How often to check for stale entries.
rE   Nr  r   rL   r   s       r   rL   Cache.__init__  &     	()#%= 	 	
r   r   r   r#   r$   r%   r&   r'   r   r   r   rL   r)   r   r   s   @r   r  r    sC    D
 6'G*W
 
 %	

 #(
 

 
r   r  c                  J   ^  \ rS rSrSr\\\S.       SU 4S jjjrSr	U =r
$ )
AsyncCachei  zDDeprecated alias for AsyncPromptCache. Use AsyncPromptCache instead.rE   c               8   > [        5         [        TU ]	  UUUS9  g)zInitialize the deprecated AsyncCache class.

Args:
    max_size: Maximum entries in cache (LRU eviction when exceeded).
    ttl_seconds: Time before entry is considered stale.
    refresh_interval_seconds: How often to check for stale entries.
rE   Nr  r   s       r   rL   AsyncCache.__init__  r
  r   r   r   r  r   s   @r   r  r    sC    N
 6'G*W
 
 %	

 #(
 

 
r   r  r   r   )*r'   
__future__r   r   rw   loggingrH   r   r  abcr   collectionsr   collections.abcr   dataclassesr   pathlibr   typingr	   r
   r   r   r   	getLoggerra   r   r   r   r   r+   r<   r   r   r   r   r   r   r  r  r  r   r   r   <module>r     sa   #        # % !  @ @			,	- $*   # 02 - = = = 7 7 7(i:s i:XR
" R
j^I' ^ID % /1 
 2#C&S	 ! $	
 
8 2#C&S	 ! $	
 
:
K 
2
! 
r   