
    `gj
!                        d Z ddlmZ ddlZddlmZmZ ddlmZm	Z	m
Z
 e G d d             Ze G d d	             Zddd
ZdddZ	 d	 	 	 	 	 ddZddZddZdddZdddZddZddZy) ai  Rate limit tracking for inference API responses.

Captures x-ratelimit-* headers from provider responses and provides
formatted display for the /usage slash command.  Currently supports
the Nous Portal header format (also used by OpenRouter and OpenAI-compatible
APIs that follow the same convention).

Header schema (12 headers total):
    x-ratelimit-limit-requests          RPM cap
    x-ratelimit-limit-requests-1h       RPH cap
    x-ratelimit-limit-tokens            TPM cap
    x-ratelimit-limit-tokens-1h         TPH cap
    x-ratelimit-remaining-requests      requests left in minute window
    x-ratelimit-remaining-requests-1h   requests left in hour window
    x-ratelimit-remaining-tokens        tokens left in minute window
    x-ratelimit-remaining-tokens-1h     tokens left in hour window
    x-ratelimit-reset-requests          seconds until minute request window resets
    x-ratelimit-reset-requests-1h       seconds until hour request window resets
    x-ratelimit-reset-tokens            seconds until minute token window resets
    x-ratelimit-reset-tokens-1h         seconds until hour token window resets
    )annotationsN)	dataclassfield)AnyMappingOptionalc                      e Zd ZU dZdZded<   dZded<   dZded<   dZded	<   e	dd
       Z
e	dd       Ze	dd       Zy)RateLimitBucketz1One rate-limit window (e.g. requests per minute).r   intlimit	remaining        floatreset_secondscaptured_atc                H    t        d| j                  | j                  z
        S Nr   )maxr   r   selfs    K/root/.hermes/venv/lib/python3.12/site-packages/agent/rate_limit_tracker.pyusedzRateLimitBucket.used'   s    1djj4>>122    c                Z    | j                   dk  ry| j                  | j                   z  dz  S )Nr   r         Y@)r   r   r   s    r   	usage_pctzRateLimitBucket.usage_pct+   s(    ::?		DJJ&%//r   c                v    t        j                          | j                  z
  }t        d| j                  |z
        S )zCEstimated seconds remaining until reset, adjusted for elapsed time.r   )timer   r   r   )r   elapseds     r   remaining_seconds_nowz%RateLimitBucket.remaining_seconds_now1   s2     ))+ 0 003**W455r   N)returnr   r!   r   )__name__
__module____qualname____doc__r   __annotations__r   r   r   propertyr   r   r     r   r   r
   r
      sf    ;E3NIsM5K3 3 0 0
 6 6r   r
   c                      e Zd ZU dZ ee      Zded<    ee      Zded<    ee      Z	ded<    ee      Z
ded<   dZd	ed
<   dZded<   edd       Zedd       Zy)RateLimitStatez3Full rate-limit state parsed from response headers.)default_factoryr
   requests_minrequests_hour
tokens_mintokens_hourr   r   r    strproviderc                     | j                   dkD  S r   )r   r   s    r   has_datazRateLimitState.has_dataC   s    !##r   c                r    | j                   st        d      S t        j                         | j                  z
  S )Ninf)r5   r   r   r   r   s    r   age_secondszRateLimitState.age_secondsG   s*    }}<yy{T----r   N)r!   boolr"   )r#   r$   r%   r&   r   r
   r-   r'   r.   r/   r0   r   r3   r(   r5   r8   r)   r   r   r+   r+   8   s{    =$)/$JL/J%*?%KM?K"'"HJH#(#IKIKHc$ $ . .r   r+   c                Z    	 t        t        |             S # t        t        f$ r |cY S w xY wN)r   r   	TypeError
ValueErrorvaluedefaults     r   	_safe_intrA   N   s/    5<  z" s    **c                H    	 t        |       S # t        t        f$ r |cY S w xY wr;   )r   r<   r=   r>   s     r   _safe_floatrC   U   s*    U|z" s   
 !!c           	     .   | j                         D ci c]  \  }}|j                         | c}}t        d D              }|syt        j                         dd	fd}t	         |d       |dd       |d       |dd      |      S c c}}w )
zoParse x-ratelimit-* headers into a RateLimitState.

    Returns None if no rate limit headers are present.
    c              3  >   K   | ]  }|j                  d         yw)zx-ratelimit-N)
startswith).0ks     r   	<genexpr>z+parse_rate_limit_headers.<locals>.<genexpr>i   s     @1!,,~.@s   Nc           
         |  | }t        t        j                  d|             t        j                  d|             t        j                  d|                   S )Nzx-ratelimit-limit-zx-ratelimit-remaining-zx-ratelimit-reset-)r   r   r   r   )r
   rA   getrC   )resourcesuffixtaglowerednows      r   _bucketz)parse_rate_limit_headers.<locals>._bucketo   sn     
6(#GKK*<SE(BCD.DSE,J KL%gkk4Fse2L&MN	
 	
r   requestsz-1htokens)r-   r.   r/   r0   r   r3   r1   )rL   r2   rM   r2   r!   r
   )itemsloweranyr   r+   )headersr3   rH   vhas_anyrQ   rO   rP   s         @@r   parse_rate_limit_headersr[   \   s     )081qwwy!|8G @@@G
))+C	
 Z(j%08$He, ) 9s   Bc                l    | dk\  r	| dz  ddS | dk\  r	| dz  ddS | dk\  r	| dz  ddS t        |       S )zIHuman-friendly number: 7999856 -> '8.0M', 33599 -> '33.6K', 799 -> '799'.i@B z.1fMi'  i  K)r2   )ns    r   
_fmt_countr`      sZ    I~i-$A&&F{e)C""Eze)C""q6Mr   c                    t        dt        |             }|dk  r| dS |dk  rt        |d      \  }}|r| d| dS | dS t        |d      \  }}|dz  }|r| d| dS | dS )	zHSeconds -> human-friendly duration: '58s', '2m 14s', '58m 57s', '1h 2m'.r   <   si  zm mzh h)r   r   divmod)secondsrc   rd   secre   	remainders         r   _fmt_secondsrj      s    As7|A2vAw4x23!$!Bse1~1QCq'1!T?LAyRAaS1#Q<)QCq')r   c                v    t        | dz  |z        }t        dt        ||            }||z
  }dd|z   d|z   dS )uW   ASCII progress bar: [████████░░░░░░░░░░░░] 40%.r   r   [u   █u   ░])r   r   min)pctwidthfilledemptys       r   _barrs      sM    uu$%FCv&'FFNEuv~uu}oQ//r   c                H   |j                   dk  rd| d| ddS |j                  }t        |j                        }t        |j                         }t        |j                        }t        |j                        }t        |      }d| d| dd| d|dd| d	| d
| d| dS )z#Format one bucket as a single line.r   z  <r1   z  (no data) z5.1fz%  /z used  (z left, resets in ))r   r   r`   r   r   rj   r    rs   )	labelbucketlabel_widthro   r   r   r   resetbars	            r   _bucket_liner~      s    ||qE!K= )55


Cfkk"Dv||$E6++,I556E
s)Ca}@_%Qse1SJc$qxPY{Zklqkrrsttr   c           
     4   | j                   sy| j                  }|dk  rd}n"|dk  rt        |       d}nt        |       d}| j                  r| j                  j                         nd}| d| d	d
t        d| j                        t        d| j                        d
t        d| j                        t        d| j                        g}g }d| j                  fd| j                  fd| j                  fd| j                  ffD ]_  \  }}|j                  dkD  s|j                  dk\  s&t        |j                        }|j                  d| d|j                  dd|        a |r"|j                  d
       |j                  |       dj!                  |      S )z2Format rate limit state for terminal/chat display.u5   No rate limit data yet — make an API request first.   zjust nowrb   zs agoz agoProviderz Rate Limits (captured z):r1   zRequests/minzRequests/hrz
Tokens/minz	Tokens/hrzrequests/minzrequests/hrz
tokens/minz	tokens/hrr   P   u     ⚠ z at z.0fu   % — resets in 
)r5   r8   r   rj   r3   titler~   r-   r.   r/   r0   r   r   r    appendextendjoin)	stateage	freshnessprovider_labellineswarningsry   rz   r|   s	            r   format_rate_limit_displayr      s   >>F


C
Qw		r3xj&	#C().	/4~~U^^))+:N 
1)B?
^U%7%78]E$7$78
\5#3#34[%"3"34E H	++,	++,	u''(	e''(	 _v <<! 0 0B 6 !=!=>EOOfUG40@0@/EEUV[U\]^_ RX99Ur   c           
     :   | j                   sy| j                  }| j                  }| j                  }| j                  }g }|j
                  dkD  r+|j                  d|j                   d|j
                          |j
                  dkD  rT|j                  dt        |j                         dt        |j
                         dt        |j                         d       |j
                  dkD  r=|j                  dt        |j                         dt        |j
                                |j
                  dkD  rT|j                  d	t        |j                         dt        |j
                         dt        |j                         d       d
j                  |      S )z<One-line compact summary for status bars / gateway messages.zNo rate limit data.r   zRPM: rw   zRPH: z	 (resets rx   zTPM: zTPH: z | )r5   r-   r/   r.   r0   r   r   r   r`   rj   r    r   )r   rmtmrhthpartss         r   format_rate_limit_compactr      sZ   >>$			B			B			B			BE	xx!|uR\\N!BHH:67	xx!|uZ56a
2888L7MYWcdfd|d|W}V~~  A  	B	xx!|uZ56a
2888L7MNO	xx!|uZ56a
2888L7MYWcdfd|d|W}V~~  A  	B::er   )r   )r?   r   r@   r   r!   r   )r   )r?   r   r@   r   r!   r   rT   )rX   zMapping[str, str]r3   r2   r!   zOptional[RateLimitState])r_   r   r!   r2   )rg   r   r!   r2   )   )ro   r   rp   r   r!   r2   )   )ry   r2   rz   r
   r{   r   r!   r2   )r   r+   r!   r2   )r&   
__future__r   r   dataclassesr   r   typingr   r   r   r
   r+   rA   rC   r[   r`   rj   rs   r~   r   r   r)   r   r   <module>r      s   , #  ( ) ) 6 6 62 . . .* %%% %V
*0u)Xr   