
    ]~jg                        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m	Z	 ddl
mZmZmZ  ej        e          Z e	d          ZdZdZd	ZddZeeedddddZdS )a  Async retry helper used by every pipeline stage.

Design:
  - Exponential backoff with jitter (2s, 4s, 8s base).
  - Retryable errors are defined by `is_retryable` (defaults: 5xx / 429 /
    network errors from the provider layer).
  - Non-retryable errors abort the coroutine immediately.
  - Each attempt can record its error via `on_attempt` if the caller wants
    to surface it (e.g. counting retries on a `city_briefs_runs` row).
    )annotationsN)	AwaitableCallableTypeVar)NonRetryableProviderErrorProviderHttpErrorRetryableProviderErrorT   g       @g       @excBaseExceptionreturnboolc                
   t          | t                    rdS t          | t                    rdS t          | t                    r| j        dk    p
| j        dk    S t
          j        t          fD ]}t          | |          r dS dS )z-Retry only transient, provider-side failures.TFi  i  )
isinstancer	   r   r   statusasyncioTimeoutErrorConnectionError)r   retryable_types     k/Users/abstractioneditor2/ai-workstation/projects/automation/show-city-briefing-agent/app/pipeline/retry.pyis_retryabler   "   s    #-.. t#011 u#()) 6zS 5CJ#$55"/A  c>** 	44	5     )max_attempts
base_delay	max_delay
on_attempt
stage_nameawaitable_factoryCallable[[], Awaitable[T]]r   intr   floatr   r   =Callable[[int, BaseException | None], Awaitable[None]] | Noner   strc          
       K   d}d}||k     rP|dz  }	  |              d{V }|r@	  ||d           d{V  n,# t           $ r t                              dd           Y nw xY w|S # t           $ r}	|	}|r@	  |||	           d{V  n,# t           $ r t                              dd           Y nw xY wt          |	          r||k    r t	          ||d|dz
  z  z            }
|
t          j        dd          z   }
t                              d	|pd
||t          |	          j        |
           t          j
        |
           d{V  Y d}	~	nd}	~	ww xY w||k     P|J |)zRun `awaitable_factory()` with bounded retries.

    `awaitable_factory` is called fresh on every attempt so async iterators
    can be retried safely.
    r   N   z#on_attempt hook raised; suppressingT)exc_info   g      ?z5stage %s attempt %d/%d failed (%s); retrying in %.1fsz	<unnamed>)	Exceptionloggerwarningr   minrandomuniformtype__name__r   sleep)r    r   r   r   r   r   attemptlast_excresultr   delays              r   
with_retryr7   1   s0      G%)H
L
 
 1	',,........F YY$*Wd3333333333  Y Y YNN#HSWNXXXXXYM 	' 	' 	'H YY$*Wc2222222222  Y Y YNN#HSWNXXXXXY$$ <(?(?	:w{1C#DEEEFN1c222ENNG)k7LS		"E  
 -&&&&&&&&&&&&&&!	' L
 
 6 
Ns\   A( : A( &A# A( "A##A( (
E2E7B
	E
&B30E2B33BEE)r   r   r   r   )r    r!   r   r"   r   r#   r   r#   r   r$   r   r%   r   r
   )__doc__
__future__r   r   loggingr.   typingr   r   r   app.providers._commonr   r   r	   	getLoggerr1   r+   r
   DEFAULT_MAX_ATTEMPTSDEFAULT_BASE_DELAYDEFAULT_MAX_DELAYr   r7    r   r   <module>rB      s  	 	 # " " " " "    / / / / / / / / / /          
	8	$	$GCLL      $ -*(PT, , , , , , , ,r   