+
    ]Qj                        a  R t0 t ^ RIt^ RIt^RIHt ^RIHt ]P                  ! ]4      t	]
! 4       s] ^ k / s] ^k R R ltR R ltR R	 lt ! R
 R]4      tR# )    N)LLMGenerationError)LLMProviderc                0    V ^8  d   QhR\         R\        /#    excreturn	Exceptionbool)formats   "GC:\xampp\htdocs\meridian\meridian-ai\app\providers\fallback_provider.py__annotate__r      s      i D     c                |    ^ RI p\        WP                  4      '       d   R# \        WP                  4      '       dY   \	        V RR4      ;'       g    RpVR9   d   R# \        V 4      P                  4       pRV9   ;'       g    RV9   ;'       g    RV9   #   ^ RIp\        WP                  4      '       d   R# \        WP                  VP                  34      '       d9   \        V 4      P                  4       pRV9   ;'       g    R	V9   ;'       g    R
V9   #  R#   \         d     Li ; i  \         d     R# i ; i)zaReturns True for unrecoverable billing/quota errors that should permanently blacklist a provider.NTcode quotaexceeded your currentbillingcreditbalancetoo lowFinsufficient_quotaquota_exceeded)
openai
isinstanceAuthenticationErrorRateLimitErrorgetattrstrlowerImportError	anthropicBadRequestError)r   r   r   msgr%   s   &    r   _is_credit_exhaustedr(      s-   c5566c00113+11rD??c(.."Cc>WW%<%CWWyTWGWW 2c8899c44i6O6OPQQc(.."Cs?JJi3&6JJ)s:JJ R     sS   D .D 
D $D D D D, 9AD, D, D, D)(D),D;:D;c                0    V ^8  d   QhR\         R\        /# r   r
   )r   s   "r   r   r   ,   s      )  r   c                8    ^ RI p\        WP                  4      '       dO   \        V RR4      ;'       g    RpVR9  d1   \	        V 4      P                  4       pRV9  d   RV9  d
   RV9  d   R#  ^ RIp\        YP                  4      '       d1   \	        T 4      P                  4       pRT9  d   R	T9  d
   R
T9  d   R# \        T RR4      ;'       g    \        T RR4      pTR8X  d   \        T 4      '       * # R#   \         d     Li ; i  \         d     LYi ; i)zXReturns True for transient 429 rate-limit errors (retry next provider, don't blacklist).Nr   r   r   r   r   Tr   r   r   status_codestatusi  Fr   )	r   r   r    r!   r"   r#   r$   r%   r(   )r   r   r   r'   r%   r,   s   &     r   _is_rate_limitedr-   ,   s   	c00113+11rDCC#hnn&#%)3*>CZbeCec3344c(.."Cs"y';	QT@T S-.NN'#x2NF}',,,!    s)   2C: 8C: 0AD :DDDDc                0    V ^8  d   QhR\         R\        /# r   )r   int)r   s   "r   r   r   I   s      i C r   c                   \        V RR4      pVe`   \        VR/ 4      ;'       g    / pVP                  R4      ;'       g    VP                  R4      pV'       d    \        ^\        V4      4      # ^<#   \        \
        3 d     ^<# i ; i)zExtract how many seconds until this provider's rate limit resets.
Falls back to 60s (typical for Gemini/OpenAI free tier per-minute windows).
responseNheaderszretry-afterzRetry-After)r!   getmaxr/   
ValueError	TypeError)r   r1   r2   raws   &   r   _extract_retry_afterr8   I   s     sJ-H(Ir288bkk-(FFGKK,F1c#h''  	* s   A3 3BBc                      a  ] tR t^Yt o RtV 3R lR lt]V 3R lR l4       t]V 3R lR l4       t]V 3R lR	 l4       t	]V 3R
 lR l4       t
]V 3R lR l4       tRV 3R lR lltV 3R lR ltRtV tR# )FallbackProvideru*  Tries providers in priority order.

- Credit/quota exhausted → permanently blacklist the provider for this process lifetime.
- Rate limited (429, transient) → skip to next provider without blacklisting; records
  when the rate-limited provider should be available again.
- Any other error → re-raise immediately (don't try fallbacks for unexpected failures).

After a successful call, `last_used` and `skipped` expose which provider handled the
request and which ones were bypassed (with retry-after info), so callers can surface
this to the user.
c                J   < V ^8  d   QhRS[ S[S[S[3,          ,          RR/# )r   	providersr	   N)listtupler"   r   )r   __classdict__s   "r   r   FallbackProvider.__annotate__f   s*     ' '$uS+-='>"? 'D 'r   c                .    Wn         R V n        . V n        R # N)
_providers
_last_used_skipped)selfr<   s   &&r   __init__FallbackProvider.__init__f   s    #&*$&r   c                .   < V ^8  d   QhRS[ R,          /# )r   r	   Nr"   )r   r?   s   "r   r   r@   o   s      3: r   c                    V P                   # rB   )rD   rF   s   &r   	last_usedFallbackProvider.last_usedn   s    r   c                0   < V ^8  d   QhRS[ S[,          /# r   r	   )r=   dict)r   r?   s   "r   r   r@   s   s      d r   c                    V P                   # rB   )rE   rL   s   &r   skippedFallbackProvider.skippedr   s    }}r   c                0   < V ^8  d   QhRS[ S[,          /# rP   r=   r"   )r   r?   s   "r   r   r@   y   s     / /tCy /r   c                N    V P                    UUu. uF  w  rVNK	  	  upp# u uppi rB   )rC   )rF   n_s   &  r   namesFallbackProvider.namesx   s     "oo.odao...s   !c                0   < V ^8  d   QhRS[ S[,          /# rP   rV   )r   r?   s   "r   r   r@   }   s     o oS	 or   c                    \         P                   ! 4       pV P                   UUu. uF1  w  r#V\        9  g   K  \        P	                  V^ 4      V8:  g   K/  VNK3  	  upp# u uppi )r   )timerC   
_exhausted_rate_limited_untilr3   )rF   nowrX   rY   s   &   r   activeFallbackProvider.active|   sN    iik"oonoda*1DI\I`I`abdeIfjmImonnns   A A A c                0   < V ^8  d   QhRS[ S[,          /# rP   rV   )r   r?   s   "r   r   r@      s     X X49 Xr   c                   a \          Uau. uF[  o\        ;QJ d)    V3R  lV P                   4       F  '       g   K   RM	  RM! V3R  lV P                   4       4      '       g   KY  SNK]  	  up# u upi )c              3   2   <"   T F  w  rSV8H  x  K  	  R # 5irB    ).0namerY   rX   s   &  r   	<genexpr>-FallbackProvider.exhausted.<locals>.<genexpr>   s     ,Vo74Q$Yos   TF)r_   anyrC   )rF   rX   s   &`r   	exhaustedFallbackProvider.exhausted   sB    %W:a,Vdoo,V,Vdoo,V)V:WWWs   A/A/%A/&A/Nc                2   < V ^8  d   QhRS[ R,          RR/# )r   ri   Nr	   rJ   )r   r?   s   "r   r   r@      s     ( (#* ( (r   c                    V'       d.   \         P                  V4       \        P                  VR4       R# \         P	                  4        \        P	                  4        R# )zHClear exhausted/rate-limited state (all providers, or just one by name).N)r_   discardr`   popclear)rF   ri   s   &&r   resetFallbackProvider.reset   s=     t$##D$/%%'r   c                <   < V ^8  d   QhRS[ RS[S[,          RS[/# )r   system_promptmessagesr	   )r"   r=   rQ   )r   r?   s   "r   r   r@      s&     0
 0
3 0
$t* 0
 0
r   c           
        R V n         . V n        R p\        P                  ! 4       pV P                   EF  w  rVV\        9   d:   \
        P                  RV4       V P                  P                  RVRRRR /4       KJ  \        P                  V^ 4      V8  dW   \        \        V,          V,
          4      p\
        P                  RWW4       V P                  P                  RVRRRV/4       K   VP                  W4      pWPn         V P                  '       d7   \
        P                  RYPP                   U	u. uF  qR,          NK  	  up	4       Vu # 	  V P                   U	u. uF  qR,          NK  	  pp	\'        RRRP)                  V4       RV R2R7      hu up	i   \         d   p
\        T
4      '       dV   \
        P                  R	YZ4       \        P!                  T4       T P                  P                  RTRRRR /4       T
p R p
?
EK  \#        T
4      '       d[   \%        T
4      pYK,           \        T&   \
        P                  R
Y[4       T P                  P                  RTRRRT/4       T
p R p
?
EKF  h R p
?
ii ; iu up	i )Nz Skipping exhausted provider '%s'ri   reasoncredit_exhaustedretry_after_secondsz.Skipping rate-limited provider '%s' (%ds left)rate_limitedz)LLM call succeeded via '%s' (skipped: %s)u7   Provider '%s' out of credit — blacklisting. Error: %su7   Provider '%s' rate-limited for %ds — trying fallback.z"All LLM providers are unavailable.zProviders tried: z, z. Last error: z<. Add credit to a provider or wait for rate limits to reset.)detail)rD   rE   r^   rC   r_   loggerdebugappendr`   r3   r/   complete_jsoninfor   r(   warningaddr-   r8   r   join)rF   rw   rx   last_excra   ri   provider	secs_leftresultsr   retry_afterskipped_namess   &&&          r   r   FallbackProvider.complete_json   s<   %)iik"ooNDz!?F$$fdH>PRgim%no"&&tQ/#5 3D 9C ?@	Mt_$$fdHnNcen%op!//H"&===KK KTgtgtSugtbcV\T]T]gtSuv# .D -1MM:Mq6M: 0#DIIm$<#= >'j )MM
 	
' Tv  ',,NN#\^bhNN4(MM((&$BTVkmq)rs"H%c**"6s";K030A'-NN#\^bpMM((&$.Rgit)uv"H ;s?   .AF5FF!JFI>'AI9A$I98I99I>)rD   rC   rE   rB   )__name__
__module____qualname____firstlineno____doc__rG   propertyrM   rS   rZ   rb   rm   rt   r   __static_attributes____classdictcell__)r?   s   @r   r:   r:   Y   s     
' '    
 / / o o X X( (0
 0
r   r:   c                    V ^8  d   Qh/ ^ \         9   d   \        \        ,          ;R&   ^\         9   d   \        \        \        3,          ;R&   # )r   r_   r`   )__conditional_annotations__setr"   rQ   float)r   s   "r   r   r      s7      CH   + *T#u*% * r   )r   loggingr^   
exceptionsr   baser   	getLoggerr   r   r   r_   r`   r(   r-   r8   r:   r   )r   s   @r   <module>r      sX      + 			8	$ u
  )+  *8: h
{ h
r   