Ë
    tPêjºE  ã                   óÂ  — U d Z ddlZddlmZ ddlmZ ddlmZmZ ddlm	Z	 ddl
mZmZmZmZ ddlmZ dd	lmZ dd
lmZ ddlmZ ddlmZ ddlmZmZmZ ddlmZ ddlm Z  ddl!m"Z" erddl#m$Z$ ddl%m&Z&  e	ejN                  jP                  ejR                  jP                  dœ«      Z*eee+e+f      e,d<    e	dddœ«      Z-eee+e+f      e,d<   dedefd„Z. G d„ d«      Z/y)am  
Coalesced reseed of spend counters from the authoritative DB.

When a Redis spend counter expires (or is missing on a fresh pod), enforcement
must read the current spend from somewhere. The in-process management cache
(`user_api_key_cache.team_membership.spend`, etc.) is per-pod and lags DB
writes from other pods, so trusting it allows budget bypass in multi-pod
deployments. This module reseeds from the authoritative DB instead.

A per-counter singleflight lock collapses concurrent reseeds on the same pod
to one DB query per cold-cache window. The lock dict is bounded LRU to cap
memory in long-lived deployments.
é    N)ÚOrderedDict)ÚMapping)ÚdatetimeÚtimezone)ÚMappingProxyType)ÚTYPE_CHECKINGÚClassVarÚFinalÚOptional)Úverbose_proxy_logger)Ú#SPEND_COUNTER_RESEED_LOCKS_MAX_SIZE)Úduration_in_seconds)ÚLitellm_EntityType)ÚOrganizationRepository)ÚBudgetWindowSpendRepositoryÚSpendLogsRepositoryÚTeamMembershipRepository)ÚTeamRepository)ÚUserRepository)ÚVerificationTokenRepository)Ú	DualCache)ÚPrismaClient)ÚKeyÚTeamÚ_WINDOW_SPEND_ENTITY_TYPESÚapi_keyÚteam_idÚ_WINDOW_SPEND_LOG_FIELDSÚvalueÚreturnc                 ó^   — | j                   �| S | j                  t        j                  ¬«      S )N)Útzinfo)r"   Úreplacer   Úutc)r   s    új/home/oem/CyberBrain26-unified/.venv/lib/python3.12/site-packages/litellm/proxy/db/spend_counter_reseed.pyÚ_as_utcr&   :   s%   € Ø—L‘LÐ,ˆ5ÐT°%·-±-ÄxÇ|Á|°-Ó2TÐTó    c                   óî  — e Zd ZU dZ e«       Zed   ed<   dZee	j                  dz     ed<   edede	j                  fd„«       Zed	ed
   dededz  fd„«       Zededefd„«       Ze	 dd	ed
   dddedededz  f
d„«       Zed	ed
   dedededededz  fd„«       Zed	ed
   dedededz  dededz  fd„«       Zed	ed
   dededededz  f
d„«       Zed	ed
   dddededededz  dededz  fd„«       Zy)ÚSpendCounterReseeda  
    Reseeds spend counters from the authoritative DB and warms the cache,
    coalesced via per-counter singleflight locks.

    Counter key prefixes map to DB tables:
        spend:key:{token}                 -> LiteLLM_VerificationToken.spend
        spend:team:{team_id}              -> LiteLLM_TeamTable.spend
        spend:team_member:{uid}:{tid}     -> LiteLLM_TeamMembership.spend
        spend:user:{user_id}              -> LiteLLM_UserTable.spend
        spend:org:{org_id}                -> LiteLLM_OrganizationTable.spend

    End-user and tag spend counters intentionally do not reseed here. Their
    auth paths already load the corresponding objects via get_end_user_object()
    and get_tag_objects_batch(); callers pass those values as fallback_spend.
    zOrderedDict[str, asyncio.Lock]Ú_locksNÚ_registry_lockÚcounter_keyr    c              ƒ   óf  K  — t         j                  €t        j                  «       t         _        t         j                  4 ƒd {  –—†  t         j                  j                  | «      }|�2t         j                  j                  | «       |cd d d «      ƒd {  –—†  S t        j                  «       }|t         j                  | <   t        t         j                  «      t        kD  r t         j                  j                  d¬«       |cd d d «      ƒd {  –—†  S 7 ŒÑ7 Œ�7 Œ	# 1 ƒd {  –—†7  sw Y   y xY w­w)NF)Úlast)
r)   r+   ÚasyncioÚLockr*   ÚgetÚmove_to_endÚlenr   Úpopitem)r,   Úlocks     r%   Ú	_get_lockzSpendCounterReseed._get_lockR   sî   è ø€ ä×,Ñ,Ð4Ü07·±³ÔÔ-Ü%×4Ñ4÷ 		ñ 		Ü%×,Ñ,×0Ñ0°Ó=ˆDØÐÜ"×)Ñ)×5Ñ5°kÔBØ÷			÷ 		ñ 		ô
 —<‘<“>ˆDØ59Ô×%Ñ% kÑ2ÜÔ%×,Ñ,Ó-Ô0SÒSÜ"×)Ñ)×1Ñ1°uÐ1Ô=Ø÷		÷ 		ò 		øð 		øð 		ø÷ 		÷ 		ñ 		üsh   ‚AD1ÁDÁD1ÁADÂ
D1ÂDÂD1ÂA(DÄD1ÄDÄD1ÄD1ÄD1ÄD.Ä"D%Ä#D.Ä*D1Úprisma_clientr   c              ƒ   óv  K  — | €yt         j                  |«      ry	 |j                  d«      r?|t        d«      d }t	        | «      j
                  j                  d|i¬«      ƒ d{  –—† }�n~|j                  d«      r\|t        d«      d }d|vry|j                  dd«      \  }}t        | «      j
                  j                  d||d	œi¬«      ƒ d{  –—† }�n|j                  d
«      r>|t        d
«      d }t        | «      j
                  j                  d|i¬«      ƒ d{  –—† }nÂ|j                  d«      r>|t        d«      d }t        | «      j
                  j                  d|i¬«      ƒ d{  –—† }ns|j                  d«      s|j                  d«      ry|j                  d«      r>|t        d«      d }t        | «      j
                  j                  d|i¬«      ƒ d{  –—† }ny|€yt        t!        |dd«      xs d«      S 7 �Œ¢7 �Œ87 Œê7 Œ�7 Œ-# t        $ r t        j                  d|«       Y yw xY w­w)al  
        Read the authoritative spend for a counter from the DB.

        Returns the spend value (including 0.0) when the DB is reachable
        and the row exists. Returns None when prisma is unavailable, the
        row is missing, the key format is unrecognized, or the query
        raises. Callers use None to fall back to a caller-supplied source.
        Nú
spend:key:Útoken©Úwherezspend:team_member:ú:é   Úuser_id_team_id)Úuser_idr   úspend:team:r   zspend:user:r@   zspend:end_user:z
spend:tag:z
spend:org:Úorganization_idz)SpendCounterReseed.from_db: failed for %sÚspendç        )r)   Ú_is_key_or_team_window_counterÚ
startswithr3   r   ÚtableÚfind_uniqueÚrsplitr   r   r   r   Ú	Exceptionr   Ú	exceptionÚfloatÚgetattr)r7   r,   r:   ÚrowÚsuffixr@   r   Úorg_ids           r%   Úfrom_dbzSpendCounterReseed.from_dba   sL  è ø€ ð Ð Øô ×<Ñ<¸[ÔIØð	Ø×%Ñ% lÔ3Ø*¬3¨|Ó+<Ð+>Ð?�Ü7¸ÓF×LÑL×XÑXÐ`gÐinÐ_oÐXÓp×p’Ø×'Ñ'Ð(<Ô=Ø +¬CÐ0DÓ,EÐ,GÐ H�Ø˜fÑ$ØØ#)§=¡=°°aÓ#8Ñ �˜Ü4°]ÓC×IÑI×UÑUØ,¸'ÈgÑ.VÐWð Vó ÷ ’ð ×'Ñ'¨Ô6Ø%¤c¨-Ó&8Ð&:Ð;�Ü*¨=Ó9×?Ñ?×KÑKÐS\Ð^eÐRfÐKÓg×g‘Ø×'Ñ'¨Ô6Ø%¤c¨-Ó&8Ð&:Ð;�Ü*¨=Ó9×?Ñ?×KÑKÐS\Ð^eÐRfÐKÓg×g‘Ø×'Ñ'Ð(9Ô:¸k×>TÑ>TÐUaÔ>bØØ×'Ñ'¨Ô5Ø +¬C°Ó,=Ð,?Ð @�Ü2°=ÓA×GÑG×SÑSÐ[lÐntÐZuÐSÓv×v‘àð ˆ;ØÜ”W˜S '¨3Ó/Ò6°3Ó7Ð7ð7 qùðùð
 høð høð
 wùô ò 	Ü ×*Ñ*Ð+VÐXcÔdÙð	üsŸ   ‚H9�A	H Á&HÁ')H ÂH9ÂAH ÃHÃAH Ä#HÄ$AH Å2HÅ3'H ÆH9ÆA	H Ç$HÇ%H Ç*H9ÈH ÈH ÈH ÈH ÈH ÈH6È3H9È5H6È6H9c                 ó¦   — dD ]<  }| j                  |«      sŒ| j                  d«      \  }}}|r|s y	 t        |«        y y# t        $ r Y  yw xY w)N)r9   rA   z:window:FT)rF   Ú
rpartitionr   rJ   )r,   ÚprefixÚ_Ú	separatorÚdurations        r%   rE   z1SpendCounterReseed._is_key_or_team_window_counter’   sl   € à3ò 
	ˆFØ×)Ñ)¨&Ô1ØØ%0×%;Ñ%;¸JÓ%GÑ"ˆAˆy˜(Ù¡HÙðÜ# HÔ-ñ ð
	ð øô ò Úðús   ´AÁ	AÁAÚspend_counter_cacher   Úrequire_cache_warmc              ƒ   óX  K  — t         j                  |«      ƒ d{  –—† }|4 ƒd{  –—†  d}|j                  �E	 |j                  j                  |¬«      ƒ d{  –—† }|�t	        |«      cddd«      ƒd{  –—†  S d}|s:|j                  j                  |¬«      }|�t	        |«      cddd«      ƒd{  –—†  S t         j                  | |«      ƒ d{  –—† }|€	 ddd«      ƒd{  –—†  yt	        |«      }	 |j                  �Ž|j                  j                  ||d¬«      ƒ d{  –—† }	|	rt	        |«      }n<|j                  j                  |¬«      ƒ d{  –—† }
|
�t	        |
«      n
t	        |«      }|j                  j                  ||¬«       n|j                  ||d¬«      ƒ d{  –—†  |cddd«      ƒd{  –—†  S 7 �Œ¦7 �Œž7 �Œn7 �ŒT# t
        $ r Y �Œ[w xY w7 �Œ)7 �Œ7 Œü7 Œ¿7 Œ�7 Œ?# t
        $ r t        j                  d|«       |r‚ Y Œaw xY w7 ŒW# 1 ƒd{  –—†7  sw Y   yxY w­w)	ao  
        Reseed a cold spend counter from the DB and warm the cache,
        coalesced via a per-counter lock so concurrent callers (read path
        + write path) collapse to one DB query per cold-cache window.

        Returns the spend value (including 0.0 from a fresh budget reset)
        when the DB read succeeds, or None when the DB is unavailable.
        NF©ÚkeyT©r\   r   Únx©r\   r   )r\   r   Úrefresh_ttlz7SpendCounterReseed.coalesced: failed to warm counter %s)r)   r6   Úredis_cacheÚasync_get_cacherL   rJ   Úin_memory_cacheÚ	get_cacherQ   Úasync_set_cacheÚ	set_cacheÚasync_increment_cacher   rK   )r7   rX   r,   rY   r5   Úredis_clean_missÚvalÚdb_spendÚcurrent_valueÚseededÚcacheds              r%   Ú	coalescedzSpendCounterReseed.coalesced¡   s^  è ø€ ô /×8Ñ8¸ÓE×EˆØ÷ 4	!ñ 4	!ð  %ÐØ"×.Ñ.Ð:ðØ 3× ?Ñ ?× OÑ OÐT_Ð OÓ `×`�CØ�Ü$ S›z÷4	!÷ 4	!ñ 4	!ð (,Ð$ñ $Ø)×9Ñ9×CÑCÈÐCÓT�Ø�?Ü  ›:÷4	!÷ 4	!ñ 4	!ô" %7×$>Ñ$>¸}ÈkÓ$Z×ZˆHØÐØ÷'4	!÷ 4	!ð 4	!ô4 $)¨£?ˆMðØ&×2Ñ2Ð>Ø*=×*IÑ*I×*YÑ*YØ'Ø&Øð +Zó +÷ %�Fñ
 Ü(-¨h«™à.A×.MÑ.M×.]Ñ.]ÐbmÐ.]Ó.n×(n˜Ø9?Ð9K¬¨f¬ÔQVÐW_ÓQ`˜Ø'×7Ñ7×AÑAØ'Ø+ð Bõ ð
 .×CÑCÈÐ[cÐquÐCÓv×vÐvð !÷i4	!÷ 4	!ñ 4	!ð Fùð4	!ùð aùð4	!úô !ò Úðúð4	!ùð" [ùð#4	!øð:%øð )oøð wùÜò Ü$×.Ñ.ØMØôñ &Øñ &ðúð[4	!ø÷ 4	!÷ 4	!ñ 4	!üsP  ‚H*›Gœ
H*¦G§H*ªHºGÁGÁGÁ*H*Á6G
Á7H*Á<GÁ>*HÂ(H*Â4GÂ5H*Â:HÃG ÃHÃH*Ã&G#Ã'H*Ã,HÃ8-G+Ä%G%Ä&1G+ÅG'ÅAG+Æ)G)Æ*G+Æ.HÆ/H*Æ;HÆ<H*ÇH*ÇGÇ
H*Ç	GÇHÇGÇHÇH*Ç HÇ#H*Ç%G+Ç'G+Ç)G+Ç+"HÈHÈHÈHÈH*ÈH'ÈHÈH'È#H*Úentity_typeÚ	entity_idÚwindow_durationÚexpected_window_startc              ƒ   óv  K  — | €yt         j                  |«      }|€y	 t        | «      j                  j	                  d|||dœi¬«      ƒ d{  –—† }|€yt        |j                  «      t        |«      k  ryt        |j                  xs d«      S 7 ŒB# t
        $ r t        j                  d|||«       Y yw xY w­w)aõ  
        Read the maintained per-window spend row by primary key.

        Returns the row's spend only when the row belongs to the window the
        caller is enforcing, i.e. ``row.window_start >= expected_window_start``.
        A row at or past the expected start was rolled by a pod whose reset_at
        was at least as fresh as this caller's, so it is trusted; an older row
        means the window boundary was crossed and nothing has rolled the row
        yet, so its spend belongs to a previous window.

        Returns None for a missing, stale or unreadable row so the caller falls
        back to the spend-logs aggregate. ``entity_type`` is the counter-facing
        label ("Key"/"Team"); anything else has no row and returns None.
        NÚ%entity_type_entity_id_window_duration)ro   rp   rq   r;   z@SpendCounterReseed.window_from_table: failed for %s=%s window=%srD   )r   r1   r   rG   rH   rJ   r   rK   r&   Úwindow_startrL   rC   )r7   ro   rp   rq   rr   Úrow_entity_typerN   s          r%   Úwindow_from_tablez$SpendCounterReseed.window_from_tableç   sÙ   è ø€ ð, Ð ØÜ!;×!?Ñ!?ÀÓ!LˆØÐ"Øð	Ü:¸=ÓI×OÑO×[Ñ[à;Ø'6Ø%.Ø+:ñ>ðð  \ó  ÷ ˆCð$ ˆ;ØÜ�3×#Ñ#Ó$¤wÐ/DÓ'EÒEØÜ�S—Y‘YÒ% #Ó&Ð&ð-ùô ò 	Ü ×*Ñ*ØRØØØô	ñ ð	üs:   ‚B9Ÿ.B ÁBÁB Á>B9ÂB Â!B6Â3B9Â5B6Â6B9ru   c              ƒ   ó¤   K  — |�&t         j                  | ||||¬«      ƒ d{  –—† }|�|S t         j                  | |||¬«      ƒ d{  –—† S 7 Œ)7 Œ­w)aP  
        Authoritative window spend: the maintained row first, falling back to
        the spend-logs aggregate only when no current row exists.

        The aggregate range-scans an unindexed table, so it must stay a
        transitional path (window configured before the row existed) rather
        than a steady-state read.
        N)r7   ro   rp   rq   rr   )r7   ro   rp   ru   )r)   rw   Úwindow_from_spend_logs)r7   ro   rp   rq   ru   Ú
from_tables         r%   Úwindow_from_dbz!SpendCounterReseed.window_from_db  sz   è ø€ ð  Ð&Ü&8×&JÑ&JØ+Ø'Ø#Ø /Ø&2ð 'Kó '÷ !ˆJð Ð%Ø!Ð!Ü'×>Ñ>Ø'Ø#ØØ%ð	 ?ó 
÷ 
ð 	
ð!øð
ús!   ‚ A¢A£$AÁAÁAÁAc              ƒ   óì  K  — | €y t         j                  |«      }|€y ||dd|ii}	 t        | «      j                  j	                  |g|ddi¬«      ƒ d {  –—† }|sy|d   }t        |t        «      r|j                  d	«      nt        |d	d «      }t        |t        «      r|j                  d«      nt        |dd «      }	t        |	xs d«      S 7 Œw# t
        $ r t        j                  d||«       Y y w xY w­w)
NÚ	startTimeÚgterC   T)Úbyr<   Úsumz;SpendCounterReseed.window_from_spend_logs: failed for %s=%srD   r   Ú_sum)r   r1   r   rG   Úgroup_byrJ   r   rK   Ú
isinstanceÚdictrM   rL   )
r7   ro   rp   ru   Úgroup_fieldr<   ÚresponseÚ	first_rowÚsum_rowrC   s
             r%   ry   z)SpendCounterReseed.window_from_spend_logs=  s  è ø€ ð Ð Øä5×9Ñ9¸+ÓFˆØÐØà˜Ø˜% Ð.ð
ˆð
	Ü$7¸Ó$F×$LÑ$L×$UÑ$UØ�=ØØ˜d�Oð %Vó %÷ ˆHñ ØØ# A™;ˆ	Ü2<¸YÌÔ2M˜Ÿ™ vÔ.ÔSZÐ[dÐflÐnrÓSsˆÜ/9¸'Ä4Ô/H�w—{‘{ 7Ô+ÌgÐV]Ð_fÐhlÓNmˆÜ�U’\˜cÓ"Ð"ð%ùô
 ò 	Ü ×*Ñ*ØMØØôñ
 ð	üs;   ‚$C4§-C ÁCÁC ÁA3C4ÃC Ã C1Ã.C4Ã0C1Ã1C4c              ƒ   ój  K  — t         j                  |«      ƒ d {  –—† }|4 ƒd {  –—†  d}|j                  �E	 |j                  j                  |¬«      ƒ d {  –—† }	|	�t	        |	«      cd d d «      ƒd {  –—†  S d}|s:|j                  j                  |¬«      }	|	�t	        |	«      cd d d «      ƒd {  –—†  S t         j                  | ||||¬«      ƒ d {  –—† }
|
€	 d d d «      ƒd {  –—†  y 	 |j                  � |j                  j                  ||
d¬«      ƒ d {  –—† }|r|
}nW|j                  j                  |¬«      ƒ d {  –—† }|€&|j                  j                  ||
¬«      ƒ d {  –—† }nt	        |«      }|j                  j                  ||¬«       n|j                  ||
¬«      ƒ d {  –—†  |
cd d d «      ƒd {  –—†  S 7 �Œ°7 �Œ¨7 �Œx7 �Œ^# t
        $ r Y �Œew xY w7 �Œ37 �Œ7 �Œ7 ŒÑ7 Œª7 Œ…7 ŒB# t
        $ r t        j                  d|«       ‚ w xY w7 ŒV# 1 ƒd {  –—†7  sw Y   y xY w­w)NFr[   T)r7   ro   rp   rq   ru   r]   r_   z>SpendCounterReseed.coalesced_window: failed to warm counter %s)r)   r6   ra   rb   rL   rJ   rc   rd   r{   re   Úasync_incrementrf   rg   r   rK   )r7   rX   r,   ro   rp   rq   ru   r5   rh   ri   Úwindow_spendrl   rk   Úcurrent_cached_values                 r%   Úcoalesced_windowz#SpendCounterReseed.coalesced_windowd  s‚  è ø€ ô /×8Ñ8¸ÓE×EˆØ÷ 6	 ñ 6	 Ø$ÐØ"×.Ñ.Ð:ðØ 3× ?Ñ ?× OÑ OÐT_Ð OÓ `×`�CØ�Ü$ S›z÷6	 ÷ 6	 ñ 6	 ð (,Ð$ñ $Ø)×9Ñ9×CÑCÈÐCÓT�Ø�?Ü  ›:÷6	 ÷ 6	 ñ 6	 ô );×(IÑ(IØ+Ø'Ø#Ø /Ø)ð )Jó )÷ #ˆLð Ð#Ø÷/6	 ÷ 6	 ð 6	 ð0Ø&×2Ñ2Ð>Ø*=×*IÑ*I×*YÑ*YØ'Ø*Øð +Zó +÷ %�Fñ
 Ø(4™à5H×5TÑ5T×5dÑ5dÐitÐ5dÓ5u×/uÐ,Ø/Ð7Ø2E×2QÑ2Q×2aÑ2aØ$/Ø&2ð 3bó 3÷ -™Mô
 -2Ð2FÓ,G˜MØ'×7Ñ7×AÑAØ'Ø+ð Bõ ð
 .×CÑCÈÐ[gÐCÓh×hÐhð  ÷m6	 ÷ 6	 ñ 6	 ð Fùð6	 ùð aùð	6	 úô !ò Úðúð6	 ùð#ùð6	 ùð4%øð 0vøð-øð iùÜò Ü$×.Ñ.ØTØôð ðúða6	 ø÷ 6	 ÷ 6	 ñ 6	 üsP  ‚H3›Gœ
H3¦G§H3ªHºGÁGÁGÁ*H3Á6GÁ7H3Á<GÁ>*HÂ(H3Â4G'Â5H3Â:HÃG*ÃHÃH3Ã*G-Ã+H3Ã1-G8ÄG0Ä(G8ÅG2Å&G8Å.G4Å/AG8Æ3G6Æ4G8Æ8HÆ9H3ÇHÇH3ÇH3ÇGÇH3Ç	G$Ç HÇ#G$Ç$HÇ'H3Ç*HÇ-H3Ç0G8Ç2G8Ç4G8Ç6G8Ç8!HÈHÈH3ÈH0È$H'È%H0È,H3)F)Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   r*   r	   Ú__annotations__r+   r/   r0   ÚstaticmethodÚstrr6   r   rL   rQ   ÚboolrE   rn   r   rw   r{   ry   r�   © r'   r%   r)   r)   >   s{  … ññ  :E»€FˆHÐ5Ñ6ÓFØ48€N�H˜WŸ\™\¨DÑ0Ñ1Ó8àð Sð ¨W¯\©\ò ó ðð ð.8 X¨nÑ%=ð .8ÈCð .8ÐTYÐ\`ÑT`ò .8ó ð.8ð` ð°Cð ¸Dò ó ðð ð
 $)ñ	C!Ø Ñ/ðC!à(ðC!ð ðC!ð !ð	C!ð
 
�‰òC!ó ðC!ðJ ð2'Ø Ñ/ð2'àð2'ð ð2'ð ð	2'ð
  (ð2'ð 
�‰ò2'ó ð2'ðh ð
Ø Ñ/ð
àð
ð ð
ð ˜t™ð	
ð
 ð
ð 
�‰ò
ó ð
ð@ ð$#Ø Ñ/ð$#àð$#ð ð$#ð ð	$#ð
 
�‰ò$#ó ð$#ðL ð@ Ø Ñ/ð@ à(ð@ ð ð@ ð ð	@ ð
 ð@ ð ˜t™ð@ ð ð@ ð 
�‰ò@ ó ñ@ r'   r)   )0r‘   r/   Úcollectionsr   Úcollections.abcr   r   r   Útypesr   Útypingr   r	   r
   r   Úlitellm._loggingr   Úlitellm.constantsr   Ú*litellm.litellm_core_utils.duration_parserr   Úlitellm.proxy._typesr   Ú,litellm.repositories.organization_repositoryr   Ú'litellm.repositories.table_repositoriesr   r   r   Ú$litellm.repositories.team_repositoryr   Ú$litellm.repositories.user_repositoryr   Ú2litellm.repositories.verification_token_repositoryr   Úlitellm.caching.dual_cacher   Úlitellm.proxy.utilsr   ÚKEYr   ÚTEAMr   r”   r’   r   r&   r)   r–   r'   r%   ú<module>r¨      sé   ðòó Ý #Ý #ß 'Ý "ß ;Ó ;å 1Ý AÝ JÝ 3Ý O÷ñ õ
 @Ý ?õñ Ý4Ý0ñ 8Hà!×%Ñ%×+Ñ+Ø"×'Ñ'×-Ñ-ñó8Ð ˜E '¨#¨s¨(Ñ"3Ñ4ó ñ 6FàØñó6Ð ˜% ¨¨S¨Ñ 1Ñ2ó ðU�8ð U ó U÷g ò g r'   