
    NQj                        d Z ddlZddlZddlZddlmZ da ej                    ZdZ	dZ
dddd	d
ededededef
dZdedefdZdedz  dedz  dedefdZe
dd
ededz  dedz  dedededeeedz  f         fdZe
fdedefdZdS )u   Retry utilities — jittered backoff for decorrelated retries.

Replaces fixed exponential backoff with jittered delays to prevent
thundering-herd retry spikes when multiple sessions hit the same
rate-limited provider concurrently.
    N)Any)g      >@g      N@g     V@      ^@   g      @r   g      ?
base_delay	max_delayjitter_ratioattemptr   r   r	   returnc                n   t           5  t          dz  at          }ddd           n# 1 swxY w Y   t          d| dz
            }|dk    s|dk    r|}nt          |d|z  z  |          }t	          j                    |dz  z  dz  }t          j        |          }|                    d||z            }	||	z   S )aC  Compute a jittered exponential backoff delay.

    Args:
        attempt: 1-based retry attempt number.
        base_delay: Base delay in seconds for attempt 1.
        max_delay: Maximum delay cap in seconds.
        jitter_ratio: Fraction of computed delay to use as random jitter
            range.  0.5 means jitter is uniform in [0, 0.5 * delay].

    Returns:
        Delay in seconds: min(base * 2^(attempt-1), max_delay) + jitter.

    The jitter decorrelates concurrent retries so multiple sessions
    hitting the same provider don't all retry at the same instant.
       Nr   ?      l   yn< l    )	_jitter_lock_jitter_countermaxmintimetime_nsrandomRandomuniform)
r
   r   r   r	   tickexponentdelayseedrngjitters
             5/home/alina/.hermes/hermes-agent/agent/retry_utils.pyjittered_backoffr    $   s    . 
  1               1gk""H2~~qJ!x-0)<< LNNdZ/0J>D
-

C[[L5011F6>s   &**errorc                     | t          | dd          t          | dd          t          | dd          g}d                    d |D                                                       S )zCBest-effort flattened provider error text for retry classification.messageNbodyresponse c              3   8   K   | ]}|t          |          V  d S )N)str).0parts     r   	<genexpr>z_error_text.<locals>.<genexpr>U   s-      DD$43CCII3C3C3C3CDD    )getattrjoinlower)r!   partss     r   _error_textr1   M   sj     	y$''vt$$z4((	E 88DD%DDDDDJJLLLr,   base_urlmodelc                     | pd                                 }|pd                                 }t          |dd          }t          |          }|dk    od|v od|v od|v pd|v S )	aL  Return True for Z.AI Coding Plan transient overload 429s.

    The coding-plan endpoint reports overload as HTTP 429 with body code 1305
    and message "The service may be temporarily overloaded...". Treat only
    that narrow shape specially so ordinary quota/billing 429s still fail fast
    through the existing classifier.
     status_codeNi  zapi.z.ai/api/coding/paas/v4zglm-5.21305ztemporarily overloaded)r/   r-   r1   )r2   r3   r!   base
model_namestatustexts          r   is_zai_coding_overload_errorr<   X   s     N!!##D+2$$&&JUM400FuD# 	A)T1	A#	A t^?74?	r,   short_attemptsdefault_waitr>   c                    t          |||          s|dfS | |k    r|dfS t          | |z
  dz
  t          t                    dz
            }t          |         }t	          d||d          dfS )u7  Provider-aware rate-limit backoff.

    For most providers this returns ``default_wait`` unchanged. For Z.AI
    Coding Plan GLM-5.2 overloads, keep the first ``short_attempts`` retries on
    the normal short exponential schedule, then switch to progressively longer
    waits (30s → 60s → 90s → 120s, capped) plus light jitter.

    ``attempt`` is 1-based, matching the retry loop's logged attempt number.
    Returns ``(wait_seconds, reason_label)`` where ``reason_label`` is suitable
    for status/log decoration when a provider-specific policy fired.
    )r2   r3   r!   Nzai_coding_overload_shortr   g?r   zai_coding_overload_long)r<   r   len!_ZAI_CODING_OVERLOAD_LONG_BACKOFFr    )r
   r2   r3   r!   r?   r>   idxr   s           r   adaptive_rate_limit_backoffrF   l   s    ( (eTTT "T!!.  888
g&*C0Q,R,RUV,V
W
WC237J A*
Y\]]]_yyyr,   c                 6    | t          t                    z   dz   S )u  Retry-loop ceiling needed for the full Z.AI overload backoff schedule.

    The adaptive policy runs ``short_attempts`` short retries, then walks the
    long-backoff table one entry per subsequent attempt. The retry loop gives
    up as soon as ``retry_count >= ceiling`` — and that check runs *before* the
    attempt's backoff is computed — so the ceiling must sit one past the final
    long-backoff entry for every long tier to actually execute.

    With the default ``api_max_retries`` (3) equal to ``short_attempts`` (3),
    the loop always gave up before reaching the long tier, leaving the whole
    long-backoff schedule as dead code. Callers extend the ceiling to this
    value for Z.AI Coding overload 429s so the 30/60/90/120s waits run.
    r   )rC   rD   r=   s    r   !zai_coding_overload_retry_ceilingrH      s     C ABBBQFFr,   )__doc__r   	threadingr   typingr   r   Lockr   rD   #_ZAI_CODING_OVERLOAD_SHORT_ATTEMPTSintfloatr    r(   r1   boolr<   tuplerF   rH    r,   r   <module>rS      s               
 y~ %> ! '( # & & && & 	&
 & & & & &RMs Ms M M M McDj t TW \`    6 >z z zz Djz :	z
 z z z 5#*z z z z@ =` G Gc Gdg G G G G G Gr,   