§
    ÷žyj
!  ã                  óê   — 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d0d„Zd1d„Zd2d3d!„Zd4d5d&„Zd6d(„Zd6d)„ZdS )7ai  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dS )ÚRateLimitBucketz1One rate-limit window (e.g. requests per minute).r   ÚintÚlimitÚ	remainingç        ÚfloatÚreset_secondsÚcaptured_atÚreturnc                ó<   — t          d| j        | j        z
  ¦  «        S ©Nr   )Úmaxr   r   ©Úselfs    ú>/home/ragecks/.hermes/hermes-agent/agent/rate_limit_tracker.pyÚusedzRateLimitBucket.used'   s   € å�1�d”j 4¤>Ñ1Ñ2Ô2Ð2ó    c                ó@   — | j         dk    rdS | j        | j         z  dz  S )Nr   r   ç      Y@)r   r   r   s    r   Ú	usage_pctzRateLimitBucket.usage_pct+   s&   € àŒ:˜Š?ˆ?Ø�3Ø”	˜DœJÑ&¨%Ñ/Ð/r   c                óh   — 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   s/   € õ ”)‘+”+ Ô 0Ñ0ˆÝ�3˜Ô*¨WÑ4Ñ5Ô5Ð5r   N)r   r   ©r   r   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   Ú__annotations__r   r   r   Úpropertyr   r   r!   © r   r   r
   r
      sº   € € € € € € à;Ð;à€E€N€N€N�NØ€IÐÐÐÑØ€MÐÐÐÑØ€KÐÐÐÑàð3ð 3ð 3ñ „Xð3ð ð0ð 0ð 0ñ „Xð0ð
 ð6ð 6ð 6ñ „Xð6ð 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dS )ÚRateLimitStatez3Full rate-limit state parsed from response headers.)Údefault_factoryr
   Úrequests_minÚrequests_hourÚ
tokens_minÚtokens_hourr   r   r   Ú ÚstrÚproviderr   Úboolc                ó   — | j         dk    S r   )r   r   s    r   Úhas_datazRateLimitState.has_dataC   s   € àÔ !Ò#Ð#r   c                ód   — | j         st          d¦  «        S t          j        ¦   «         | j        z
  S )NÚinf)r6   r   r   r   r   s    r   Úage_secondszRateLimitState.age_secondsG   s,   € àŒ}ð 	 Ý˜‘<”<ÐÝŒy‰{Œ{˜TÔ-Ñ-Ð-r   N)r   r4   r"   )r#   r$   r%   r&   r   r
   r-   r'   r.   r/   r0   r   r3   r(   r6   r9   r)   r   r   r+   r+   8   sô   € € € € € € à=Ð=à$) E¸/Ð$JÑ$JÔ$J€LÐJÐJÐJÑJØ%* U¸?Ð%KÑ%KÔ%K€MÐKÐKÐKÑKØ"' %¸Ð"HÑ"HÔ"H€JÐHÐHÐHÑHØ#( 5¸Ð#IÑ#IÔ#I€KÐIÐIÐIÑIØ€KÐÐÐÑØ€HÐÐÐÑàð$ð $ð $ñ „Xð$ð ð.ð .ð .ñ „Xð.ð .ð .r   r+   Úvaluer   Údefaultr   r   c                ón   — 	 t          t          | ¦  «        ¦  «        S # t          t          f$ r |cY S w xY w©N)r   r   Ú	TypeErrorÚ
ValueError©r:   r;   s     r   Ú	_safe_intrA   N   sD   € ðÝ•5˜‘<”<Ñ Ô Ð øÝ•zÐ"ð ð ð Øˆˆˆðøøøs   ‚ ž4³4r   r   c                óT   — 	 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   ‚ ‘'¦'r1   ÚheadersúMapping[str, str]r3   r2   úOptional[RateLimitState]c           	     ó(  ‡‡— d„ |                       ¦   «         D ¦   «         Št          d„ ‰D ¦   «         ¦  «        }|sdS t          j        ¦   «         Šddˆˆfd
„}t           |d¦  «         |dd¦  «         |d¦  «         |dd¦  «        ‰|¬¦  «        S )zoParse x-ratelimit-* headers into a RateLimitState.

    Returns None if no rate limit headers are present.
    c                ó>   — i | ]\  }}|                      ¦   «         |“ŒS r)   )Úlower)Ú.0ÚkÚvs      r   ú
<dictcomp>z,parse_rate_limit_headers.<locals>.<dictcomp>f   s&   € Ð8Ð8Ð8¡  1ˆq�wŠw‰yŒy˜!Ð8Ð8Ð8r   c              3  ó@   K  — | ]}|                      d ¦  «        V — ŒdS )zx-ratelimit-N)Ú
startswith)rJ   rK   s     r   ú	<genexpr>z+parse_rate_limit_headers.<locals>.<genexpr>i   s.   è è € Ð@Ð@°1�!—,’,˜~Ñ.Ô.Ð@Ð@Ð@Ð@Ð@Ð@r   Nr1   Úresourcer2   Úsuffixr   r
   c           
     ó  •— | › |› �}t          t          ‰                     d|› �¦  «        ¦  «        t          ‰                     d|› �¦  «        ¦  «        t          ‰                     d|› �¦  «        ¦  «        ‰¬¦  «        S )Nzx-ratelimit-limit-zx-ratelimit-remaining-zx-ratelimit-reset-)r   r   r   r   )r
   rA   ÚgetrC   )rQ   rR   ÚtagÚloweredÚnows      €€r   Ú_bucketz)parse_rate_limit_headers.<locals>._bucketo   s�   ø€ ð Ð#˜6Ð#Ð#ˆÝÝ˜GŸKšKÐ(B¸SÐ(BÐ(BÑCÔCÑDÔDÝ §¢Ð,JÀSÐ,JÐ,JÑ KÔ KÑLÔLÝ% g§k¢kÐ2LÀsÐ2LÐ2LÑ&MÔ&MÑNÔNØð	
ñ 
ô 
ð 	
r   Úrequestsz-1hÚtokens)r-   r.   r/   r0   r   r3   ©r1   )rQ   r2   rR   r2   r   r
   )ÚitemsÚanyr   r+   )rD   r3   Úhas_anyrX   rV   rW   s       @@r   Úparse_rate_limit_headersr_   \   sÖ   øø€ ð 9Ð8¨¯ª©¬Ð8Ñ8Ô8€Gõ Ð@Ð@¸Ð@Ñ@Ô@Ñ@Ô@€GØð Øˆtå
Œ)‰+Œ+€Cð	
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
õ Ø�W˜ZÑ(Ô(Ø�g˜j¨%Ñ0Ô0Ø�7˜8Ñ$Ô$Ø�G˜H eÑ,Ô,ØØðñ ô ð r   Únc                óz   — | 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   )r`   s    r   Ú
_fmt_countrd   ‡   sj   € àˆI‚~€~Ø�i‘-Ð&Ð&Ð&Ð&Ð&ØˆF‚{€{Ø�e‘)Ð"Ð"Ð"Ð"Ð"ØˆE‚z€zØ�e‘)Ð"Ð"Ð"Ð"Ð"Ýˆq‰6Œ6€Mr   Úsecondsc                óð   — t          dt          | ¦  «        ¦  «        }|dk     r|› d�S |dk     r"t          |d¦  «        \  }}|r|› d|› d�n|› d�S t          |d¦  «        \  }}|dz  }|r|› d|› d�n|› d�S )	zHSeconds -> human-friendly duration: '58s', '2m 14s', '58m 57s', '1h 2m'.r   é<   Úsi  zm Úmzh Úh)r   r   Údivmod)re   rh   ri   Úsecrj   Ú	remainders         r   Ú_fmt_secondsrn   ’   s¡   € åˆA�s�7‰|Œ|ÑÔ€AØˆ2‚v€vØˆwˆwˆwˆØˆ4‚x€xÝ˜˜2‘”‰ˆˆ3Ø!$Ð1�!ˆ~ˆ~�sˆ~ˆ~ˆ~ˆ~¨Q¨'¨'¨'Ð1Ý˜!˜T‘?”?�L€A€yØ�R‰€AØÐ)ˆaˆ<ˆ<�1ˆ<ˆ<ˆ<ˆ< Q ' ' 'Ð)r   é   ÚpctÚwidthc                óŽ   — 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)rp   rq   ÚfilledÚemptys       r   Ú_barrx   Ÿ   sX   € å��u‘˜uÑ$Ñ%Ô%€FÝ�•C˜˜vÑ&Ô&Ñ'Ô'€FØ�F‰N€EØ/ˆu�v‰~Ð/˜u u™}Ð/Ð/Ð/Ð/r   é   ÚlabelÚbucketÚlabel_widthc                ó2  — |j         dk    r
d| d|› �›d�S |j        }t          |j        ¦  «        }t          |j         ¦  «        }t          |j        ¦  «        }t          |j        ¦  «        }t          |¦  «        }d| d|› �›d|› d|d›d|› d|› d	|› d
|› d�S )z#Format one bucket as a single line.r   z  Ú<z  (no data)Ú z5.1fz%  ú/z used  (z left, resets in ú))r   r   rd   r   r   rn   r!   rx   )	rz   r{   r|   rp   r   r   r   ÚresetÚbars	            r   Ú_bucket_liner„   §   sÍ   € à„|�qÒÐØ5�EÐ5˜KÐ5Ð5Ð5Ð5Ð5Ð5à
Ô
€CÝ�f”kÑ"Ô"€DÝ�v”|Ñ$Ô$€EÝ˜6Ô+Ñ,Ô,€IÝ˜Ô5Ñ6Ô6€Eå
ˆs‰)Œ)€CØt�Ðt˜ÐtÐtÐtÐt sÐtÐt¨SÐtÐtÐt¸$ÐtÐtÀÐtÐtÐPYÐtÐtÐlqÐtÐtÐtÐtr   Ústatec           
     óö  — | j         sdS | j        }|dk     rd}n+|dk     rt          |¦  «        › d�}nt          |¦  «        › d�}| j        r| 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 ]S\  }}|j        dk    rC|j        dk    r8t          |j        ¦  «        }|                     d|› d|j        d›d|› �¦  «         ŒT|r*|                     d
¦  «         |                     |¦  «         d                     |¦  «        S )z2Format rate limit state for terminal/chat display.u5   No rate limit data yet â€” make an API request first.é   zjust nowrg   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 Ú
)r6   r9   r   rn   r3   Útitler„   r-   r.   r/   r0   r   r   r!   ÚappendÚextendÚjoin)	r…   ÚageÚ	freshnessÚprovider_labelÚlinesÚwarningsrz   r{   r‚   s	            r   Úformat_rate_limit_displayr”   ¶   sÉ  € àŒ>ð GØFÐFà
Ô
€CØ
ˆQ‚w€wØˆ	ˆ	Ø	ˆrŠˆÝ˜3‘x”xÐ&Ð&Ð&ˆ	ˆ	å# CÑ(Ô(Ð.Ð.Ð.ˆ	à/4¬~ÐM�U”^×)Ò)Ñ+Ô+Ð+À:€Nð Ð?Ð?°)Ð?Ð?Ð?Ø
Ý�^ UÔ%7Ñ8Ô8Ý�] EÔ$7Ñ8Ô8Ø
Ý�\ 5Ô#3Ñ4Ô4Ý�[ %Ô"3Ñ4Ô4ð€Eð €Hà	˜Ô+Ð,Ø	˜Ô+Ð,Ø	�uÔ'Ð(Ø	�eÔ'Ð(ð	ð _ð _‰ˆˆvð Œ<˜!ÒÐ Ô 0°BÒ 6Ð 6Ý  Ô!=Ñ>Ô>ˆEØ�OŠOÐ] UÐ]Ð]°Ô0@Ð]Ð]Ð]ÐV[Ð]Ð]Ñ^Ô^Ð^øàð Ø�Š�RÑÔÐØ�Š�XÑÔÐà�9Š9�UÑÔÐr   c           
     óî  — | j         sdS | j        }| j        }| j        }| j        }g }|j        dk    r%|                     d|j        › d|j        › �¦  «         |j        dk    rU|                     dt          |j        ¦  «        › dt          |j        ¦  «        › dt          |j
        ¦  «        › d�¦  «         |j        dk    r?|                     dt          |j        ¦  «        › dt          |j        ¦  «        › �¦  «         |j        dk    rU|                     d	t          |j        ¦  «        › dt          |j        ¦  «        › dt          |j
        ¦  «        › d�¦  «         d
                     |¦  «        S )z<One-line compact summary for status bars / gateway messages.zNo rate limit data.r   zRPM: r€   zRPH: z	 (resets r�   zTPM: zTPH: z | )r6   r-   r/   r.   r0   r   rŒ   r   rd   rn   r!   rŽ   )r…   ÚrmÚtmÚrhÚthÚpartss         r   Úformat_rate_limit_compactr›   â   s³  € àŒ>ð %Ø$Ð$à	Ô	€BØ	Ô	€BØ	Ô	€BØ	Ô	€Bà€EØ	„x�!‚|€|Ø�ŠÐ6˜Rœ\Ð6Ð6¨B¬HÐ6Ð6Ñ7Ô7Ð7Ø	„x�!‚|€|Ø�Šð  A�Z¨¬Ñ5Ô5ð  Að  A½
À2Ä8Ñ8LÔ8Lð  Að  AÕWcÐdfÔd|ÑW}ÔW}ð  Að  Að  Añ  	Bô  	Bð  	BØ	„x�!‚|€|Ø�ŠÐN�Z¨¬Ñ5Ô5ÐNÐN½
À2Ä8Ñ8LÔ8LÐNÐNÑOÔOÐOØ	„x�!‚|€|Ø�Šð  A�Z¨¬Ñ5Ô5ð  Að  A½
À2Ä8Ñ8LÔ8Lð  Að  AÕWcÐdfÔd|ÑW}ÔW}ð  Að  Að  Añ  	Bô  	Bð  	Bà�:Š:�eÑÔÐr   )r   )r:   r   r;   r   r   r   )r   )r:   r   r;   r   r   r   r[   )rD   rE   r3   r2   r   rF   )r`   r   r   r2   )re   r   r   r2   )ro   )rp   r   rq   r   r   r2   )ry   )rz   r2   r{   r
   r|   r   r   r2   )r…   r+   r   r2   )r&   Ú
__future__r   r   Údataclassesr   r   Útypingr   r   r   r
   r+   rA   rC   r_   rd   rn   rx   r„   r”   r›   r)   r   r   ú<module>rŸ      s­  ððð ð, #Ð "Ð "Ð "Ð "Ð "à €€€Ø (Ð (Ð (Ð (Ð (Ð (Ð (Ð (Ø )Ð )Ð )Ð )Ð )Ð )Ð )Ð )Ð )Ð )ð ð6ð 6ð 6ð 6ð 6ñ 6ô 6ñ „ð6ð2 ð.ð .ð .ð .ð .ñ .ô .ñ „ð.ð*ð ð ð ð ðð ð ð ð ð ð%ð %ð %ð %ð %ðVð ð ð ð
*ð 
*ð 
*ð 
*ð0ð 0ð 0ð 0ð 0ðuð uð uð uð uð)ð )ð )ð )ðXð ð ð ð ð r   