Ë
    tPêj%  ã                   óØ   — U d Z ddlmZ ddlmZmZ ddlmZmZ ddlm	Z	 ddl
mZ ddlmZmZmZ erddlmZ  edd¬	«      Zeed
<   defd„Z G d„ d«      Zdddeddfd„Zdddeddfd„Zy)a±  
Accumulates gateway request counts (SGR) recorded at the ASGI edge and commits
them to ``LiteLLM_DailyGatewayRequests``.

Unlike the spend queues this keeps no per-request item. A count is a pure
aggregate, so requests fold into an in-memory map as they finish. Every
dimension of the key is server-chosen and drawn from a fixed set: the date, the
category, and a route that the classifier maps to one of a closed list of
strings rather than passing the raw path through. Nothing a caller sends can
add a key, so the fold and the table it commits to are bounded by (days x
routes) however much traffic arrives, and the response path carries no
unbounded queue that would block once full.
é    )Úasdict)ÚdatetimeÚtimezone)ÚTYPE_CHECKINGÚFinal)Úverbose_proxy_logger)ÚBillableCategory)ÚGatewayRequestCountsÚGatewayRequestKeyÚGatewayRequestSnapshot)ÚPrismaClient©Úsuccessful_requestsÚfailed_requestsÚ_EMPTYÚreturnc                  óf   — t        j                  t        j                  «      j	                  d«      S )Nz%Y-%m-%d)r   Únowr   ÚutcÚstrftime© ó    ún/home/oem/CyberBrain26-unified/.venv/lib/python3.12/site-packages/litellm/proxy/db/gateway_request_tracking.pyÚ	_utc_dater   !   s    € Ü�<‰<œŸ™Ó%×.Ñ.¨zÓ:Ð:r   c                   óL   — e Zd ZdZdd„Zdedededdfd„Zde	fd	„Z
d
e	ddfd„Zy)ÚGatewayRequestAccumulatorzMSink for the request-metrics middleware. ``record`` is sync and never awaits.r   Nc                 ó   — i | _         y ©N©Ú_counts)Úselfs    r   Ú__init__z"GatewayRequestAccumulator.__init__(   s	   € ØFHˆ�r   ÚcategoryÚrouteÚstatus_codec                óÚ   — t        t        «       |j                  |¬«      }| j                  j	                  |t
        «      j                  d|cxk  xr dk  nc ¬«      | j                  |<   y )N©Údater#   r$   éÈ   i,  )Ú	succeeded)r   r   Úvaluer    Úgetr   Úplus)r!   r#   r$   r%   Úkeys        r   Úrecordz GatewayRequestAccumulator.record+   sP   € Ü&¬I«KÀ(Ç.Á.ÐX]Ô^ˆØ ŸL™L×,Ñ,¨S´&Ó9×>Ñ>ÈÐP[ÖIaÐ^aÔIaÐ>Óbˆ�‰�SÒr   c                 ó,   — | j                   }i | _         |S r   r   )r!   Údraineds     r   ÚdrainzGatewayRequestAccumulator.drain/   s   € ØŸ™ˆØˆŒØˆr   Úsnapshotc                 ó  — |j                  «       D ]m  \  }}| j                  j                  |t        «      }t	        |j
                  |j
                  z   |j                  |j                  z   ¬«      | j                  |<   Œo y)aï  
        Merge un-committed counts back so the next flush retries them.

        A dropped flush would silently undercount the metric the dashboard now
        treats as the source of truth. Merging cannot grow without bound: keys
        collapse on collision, so the fold stays bounded by (date x category x
        route) however long the database is unreachable.

        This buys at-least-once, not exactly-once, and the cost is worth stating.
        The batch commits inside its context manager's ``__aexit__``, so a failure
        raised after the transaction committed (a connection dropped while reading
        the acknowledgement) restores counts that are already persisted, and the
        next flush increments them a second time. Exactly-once would need a dedup
        key the upserts could ignore on replay. For a traffic-volume metric a rare
        overcount on a dropped acknowledgement beats losing a whole interval to
        every database blip, so the trade is deliberate.
        r   N)Úitemsr    r,   r   r
   r   r   )r!   r3   r.   ÚcountsÚexistings        r   Úrestorez!GatewayRequestAccumulator.restore4   so   € ð$ $Ÿ>™>Ó+ò 	‰KˆC�Ø—|‘|×'Ñ'¨¬VÓ4ˆHÜ 4Ø$,×$@Ñ$@À6×C]ÑC]Ñ$]Ø (× 8Ñ 8¸6×;QÑ;QÑ Qô!ˆD�L‰L˜Òñ	r   )r   N)Ú__name__Ú
__module__Ú__qualname__Ú__doc__r"   r	   ÚstrÚintr/   r   r2   r8   r   r   r   r   r   %   sT   „ ÙWóIðcÐ"2ð c¸3ð cÈSð cÐUYó cðÐ-ó ð
Ð 6ð ¸4ô r   r   Úprisma_clientr   r3   Nc           	   ƒ   óú  K  — |syt        |j                  «       d„ ¬«      }| j                  j                  «       4 ƒd{  –—† }|D ]h  \  }}t	        |«      }|j
                  j                  d|ii |¥|j                  |j                  dœ¥d|j                  id|j                  idœdœ¬«       Œj ddd«      ƒd{  –—†  t        j                  d	t        |«      «       y7 Œ¡7 Œ&# 1 ƒd{  –—†7  sw Y   Œ6xY w­w)
z8Upsert one incrementing row per (date, category, route).Nc                 óZ   — | d   j                   | d   j                  | d   j                  fS )Nr   r'   )Úitems    r   ú<lambda>z/commit_gateway_requests_to_db.<locals>.<lambda>W   s-   € ÀÀQÁÇÁÈdÐSTÉg×N^ÑN^Ð`dÐefÑ`g×`mÑ`mÐ?n€ r   )r.   Údate_category_router   Ú	increment)ÚcreateÚupdate)ÚwhereÚdataz7Gateway request tracking - committed %d aggregated rows)Úsortedr5   ÚdbÚbatch_r   Úlitellm_dailygatewayrequestsÚupsertr   r   r   ÚdebugÚlen)r?   r3   ÚorderedÚbatcherr.   r6   Úcolumnss          r   Úcommit_gateway_requests_to_dbrT   N   s
  è ø€ ñ Øä˜HŸN™NÓ,Ñ2nÔo€Gð
 ×Ñ×&Ñ&Ó(÷ ð ¨GØ"ò 	‰KˆC�Ü˜S“kˆGØ×0Ñ0×7Ñ7Ø,¨gÐ6ðØ!ðà/5×/IÑ/IØ+1×+AÑ+Aòð 1<¸V×=WÑ=WÐ/XØ,7¸×9OÑ9OÐ+Pññ
ð 8õ ð	÷÷ ô$ ×ÑÐXÔZ]Ð^eÓZfÕgð%øð ø÷ ÷ ñ üsH   ‚>C;Á C"ÁC;ÁA.C&Â2C;Â=C$Â>%C;Ã$C;Ã&C8Ã,C/Ã-C8Ã4C;Úaccumulatorc              ƒ   óÞ   K  — |j                  «       }	 t        | |¬«      ƒ d{  –—†  y7 Œ# t        $ r5 |j                  |«       t	        j
                  dt        |«      d¬«       Y yw xY w­w)a  
    Scheduler entrypoint. Never raises: a metering failure must not kill the job.

    ``CancelledError`` is deliberately not caught, so a flush cancelled during
    shutdown drops its snapshot rather than restoring counts onto an accumulator
    the process is about to discard.
    )r?   r3   NzOGateway request tracking - failed to commit %d rows, retrying on the next flushT)Úexc_info)r2   rT   Ú	Exceptionr8   r   ÚwarningrP   )r?   rU   r3   s      r   Úflush_gateway_requestsrZ   q   sc   è ø€ ð "×'Ñ'Ó)€Hð
Ü+¸-ÐRZÔ[×[Ò[ùÜò 
Ø×Ñ˜HÔ%Ü×$Ñ$Ø]Ü�‹MØ÷	
ð
üs1   ‚A-”, ¤*¥, ©A-ª, ¬;A*Á'A-Á)A*Á*A-)r<   Údataclassesr   r   r   Útypingr   r   Úlitellm._loggingr   Ú<litellm.proxy.middleware.billable_request_metrics_middlewarer	   Ú$litellm.types.proxy.gateway_requestsr
   r   r   Úlitellm.proxy.utilsr   r   Ú__annotations__r=   r   r   rT   rZ   r   r   r   ú<module>rb      sš   ðòõ ß 'ß 'å 1Ý Y÷ñ ñ Ý0á$¸ÈAÔN€ˆÓ Nð;�3ó ;÷&ñ &ðR hà!ð hð %ð hð 
ó	 hðF
Ø!ð
à*ð
ð 
ô
r   