+
    lHàj/  ã                  óÞ   € R t ^ RIHt ^ RIt^ RIt^ RIHtHtHtH	t	H
t
 ^ RIHt RtRtR]/R R	 lltR
 R ltR R ltR R ltR R ltR R ltR R ltR R ltR R lt ! R R4      tR# )uä  Shared FAL.ai SDK plumbing: lazy import, managed-gateway sync client, small helpers.

Holds the stateless atoms that every FAL-backed tool needs:

* :func:`import_fal_client` â€” lazy import + ``pm.ensure_import`` so
  ``fal_client`` isn't pulled at cold start (it added ~64 ms per CLI
  invocation when imported eagerly).
* :class:`_ManagedFalSyncClient` â€” wrapper that drives a Nous-managed
  fal-queue gateway through the standard ``fal_client.SyncClient``
  primitives.
* :func:`_normalize_fal_queue_url_format`, :func:`_extract_http_status`
  â€” small helpers used by both the managed client wrapper and
  ``_submit_fal_request``.

Stateful pieces (cache globals, ``_managed_fal_client*`` selectors,
``_submit_fal_request``) intentionally stay on
:mod:`tools.image_generation_tool`. That module is the patch target for
existing test suites (``tests/tools/test_image_generation.py``,
``tests/tools/test_managed_media_gateways.py``) and for the
``plugins/image_gen/fal/`` plugin's ``_it`` indirection â€” moving the
caches here would silently defeat ``monkeypatch.setattr(image_tool,
"_managed_fal_client", None)`` because the lookups would go against
``fal_common``'s namespace instead. See the per-rule walkthrough at
issue #26241 for details.
)ÚannotationsN)ÚAnyÚCallableÚDictÚOptionalÚUnion)Ú	urlencodeg      >@g       @Úintervalc               ó$   € V ^8„  d   QhRRRRRR/# )é   Úhandler   r	   ÚfloatÚreturn© )Úformats   "Ú5/home/ubuntu/.hermes/hermes-agent/tools/fal_common.pyÚ__annotate__r   *   s"   € ÷ ñ  #ð °Eð ÐWZñ ó    c               óV   € V P                  RVR7       F  pK  	  V P                  4       # )z¬``handle.get()`` without the SDK's 0.1 s status poll: drain ``iter_events`` at ``interval``,
then ``get()`` sees Completed on its first status check and fetches the result.F)Ú	with_logsr	   )Úiter_eventsÚget)r   r	   Ú_s   &$ r   Úwait_fal_queue_resultr   *   s.   € ð ×Ñ¨%¸(ÐÖCˆÙñ Dà�:‰:‹<Ðr   c               ó   € V ^8„  d   QhRR/# )r   r   r   r   )r   s   "r   r   r   2   s   € ÷ ñ ˜3ñ r   c                 ó¶   €  ^ RI Hp   V ! R4       ^ RIpT#   \         d     L\         d   p\        \	        T4      4      hRp?ii ; i  \         d     LFi ; i)z¥Import ``fal_client`` (via ``pm`` when available) and return
the module reference.

Callers cache the result on their own module global so tests can monkeypatch it.
)Úensure_importÚfalN)Úpmr   ÚImportErrorÚ	ExceptionÚstrÚ
fal_client)Ú_lazy_ensureÚexcr"   s      r   Úimport_fal_clientr%   2   sc   € ð(Ý4ð	(Ù˜Ôó
 ØÐøô ô 	ÙÜô 	(Üœc #›hÓ'Ð'ûð	(ûô ô ñ 	ðús/   ‚A
 ‰ —A¤A­A®AÁAÁ
AÁAc               ó    € V ^8„  d   QhRRRR/# )r   Úqueue_run_originr!   r   r   )r   s   "r   r   r   I   s   € ÷ #ñ #°cð #¸cñ #r   c                ó–   € \        T ;'       g    R 4      P                  4       P                  R4      pV'       g   \        R4      hV R2# )Ú Ú/z$Managed FAL queue origin is required)r!   ÚstripÚrstripÚ
ValueError)r'   Únormalized_origins   & r   Ú_normalize_fal_queue_url_formatr/   I   sG   € ÜÐ,×2Ð2°Ó3×9Ñ9Ó;×BÑBÀ3ÓGÐßÜÐ?Ó@Ð@ØÐ  Ð"Ð"r   c               ó    € V ^8„  d   QhRRRR/# )r   r$   ÚBaseExceptionr   zOptional[int]r   )r   s   "r   r   r   P   s   € ÷ 7ñ 7˜mð 7°ñ 7r   c                ó¼   € \        V RR4      pVe&   \        VRR4      p\        V\        4      '       d   V# \        V RR4      p\        V\        4      '       d   V# R# )zjHTTP status from httpx (``.response.status_code``) or fal_client (``.status_code``) exceptions, else None.ÚresponseNÚstatus_code)ÚgetattrÚ
isinstanceÚint)r$   r3   Ústatuss   &  r   Ú_extract_http_statusr9   P   s[   € ä�s˜J¨Ó-€HØÒÜ˜ =°$Ó7ˆÜ�fœc×"Ò"ØˆMÜ�S˜-¨Ó.€FÜ ¬×,Ò,ˆ6Ð6°$Ð6r   c               ó$   € V ^8„  d   QhRRRRRR/# )r   r$   r1   Úwhatr!   r   úOptional[str]r   )r   s   "r   r   r   [   s!   € ÷ ñ  Mð ¸ð Àñ r   c                óè  € \        V RR4      pVf   R#  VP                  4       p\        T\        4      '       d   TP                  R4      MRp\        T\        4      '       d   TP                  R4      R8w  d   R# \        TP                  R4      \        4      '       d   TP                  R4      M/ p\        TP                  R4      \        4      '       d   TP                  R4      M/ pTP                  R4      ;'       g    TP                  R4      ;'       g    R	pTP                  R4      ;'       g    R
pTP                  R4      ;'       g    R RT RT RT R2#   \         d     R# i ; i)zæHuman-readable tail for a Nous managed-gateway ``BILLING_ERROR`` response, else None.

``what`` names the rejected thing ("model", "endpoint"); the wording is shared by the image
and video callers so the two surfaces never drift.
r3   NÚerrorÚcodeÚBILLING_ERRORÚdetailsÚupstreamPayloadÚchargeIntentErrorCodeÚbilling_errorz-Nous Portal rejected the charge authorizationÚmessagezCharge authorization failedz (BILLING_ERROR; z: zu). This is a Nous Portal billing configuration issue, not a missing local API key. The managed route cannot run this zN until Nous enables its billing meter; a direct FAL_KEY is an optional bypass.)r5   Újsonr    r6   Údictr   )	r$   r;   r3   Úpayloadr>   rA   Úupstreamr?   Údetails	   &&       r   Ú_managed_fal_billing_errorrK   [   sO  € ô �s˜J¨Ó-€HØÒÙðØ—-‘-“/ˆô %/¨w¼×$=Ò$=ˆG�K‰K˜Ô À4€EÜ�eœT×"Ò" e§i¡i°Ó&7¸?Ô&JÙÜ&0°·±¸9Ó1EÄt×&LÒ&Lˆe�i‰i˜	Ô"ÐRT€GÜ1;¸G¿K¹KÐHYÓ<ZÔ\`×1aÒ1aˆw�{‰{Ð,Ô-Ðgi€HØ�<‰<˜Ó×ZÐZ 7§;¡;Ð/FÓ#G×ZÐZÈ?€DØ�\‰\˜'Ó"×UÐUÐ&U€Fà�9‰9�YÓ×@Ð@Ð#@Ð
AÐARÐSWÐRXÐXZÐ[aÐZbð c-à-1¨Fð 32ð	2ðøô ô Úðús   •E" Å"E1Å0E1c               ó    € V ^8„  d   QhRRRR/# )r   r$   r1   r   úOptional[float]r   )r   s   "r   r   r   w   s   € ÷ ñ ¨-ð ¸Oñ r   c                óÆ  € \        V RR4      pVe   \        V 4      R8w  d   R# \        VRR4      pVe$   \        VR4      '       d   VP                  R4      MRpVfJ    VP	                  4       P                  R4      p\        T\        4      '       d   TP                  R4      MRp Ve   \        V4      # R#   \
         d     R# i ; i  \        \        3 d     R# i ; i)	z²Seconds the managed gateway asked us to wait after a 429: the ``Retry-After`` header,
else the body's ``error.retryAfter``; None when the status is not 429 or neither is present.r3   Né­  Úheadersr   zRetry-Afterr>   Ú
retryAfter)r5   r9   Úhasattrr   rF   r    r6   rG   r   Ú	TypeErrorr-   )r$   r3   rP   Úrawr>   s   &    r   Ú _managed_fal_retry_after_secondsrU   w   sÛ   € ô �s˜J¨Ó-€HØÒÔ/°Ó4¸Ô;ÙÜ�h 	¨4Ó0€GØ(/Ò(;ÄÈÐQV×@WÒ@Wˆ'�+‰+�mÔ
$Ð]a€CØ
‚{ð	Ø—M‘M“O×'Ñ'¨Ó0ˆEô *4°E¼4×)@Ò)@ˆe�i‰i˜Ô%ÀdˆðØ š_Œu�S‹zÐ6°$Ð6øô	 ô 	Úð	ûô
 ”zÐ"ô Úðús*   ÁB9 Â(C Â7C Â9CÃCÃC ÃC c               ó(   € V ^8„  d   QhRRRRRRRR/# )r   r;   r!   ÚnameÚretry_afterrM   r   r   )r   s   "r   r   r   ‹   s)   € ÷ ñ ¨#ð °Sð Àð Ð[^ñ r   c                ó4   € Ve   RVR R2MRpRV  RV RV R2# )	Nzretry after ÚgÚszno Retry-After givenz'Nous Subscription gateway rate-limited z 'z' (HTTP 429; uW   ). The model is enabled â€” retry later instead of switching models or setting FAL_KEY.r   )r;   rW   rX   Úhints   &&& r   Ú_managed_fal_rate_limit_messager]   ‹   s?   € Ø.9Ò.Eˆ\˜+ a˜¨Ñ*ÐKa€Dà
1°$°°r¸$¸¸}ÈTÈFð S_ð 	_ðr   c               ó$   € V ^8„  d   QhRRRRRR/# )r   ÚsubmitzCallable[[dict[str, str]], Any]r;   r!   rW   r   )r   s   "r   r   r   “   s)   € ÷ Lñ LØ+ðLØ69ðLØADñLr   c               ót  € ^ RI Hp R F*  p V ! R\        \        P                  ! 4       4      /4      u # 	  R#   \
         dô   p\        T4      R8w  d   h \        T4      pT^8X  g   Te   T\        8”  d   \        \        YT4      4      Th\        P                  ! 4       T,           p\        P                  ! 4       T8  dj   T! 4       '       d   \        \        YT4      4      Th\        P                  ! \        R\        RT\        P                  ! 4       ,
          4      4      4       Kƒ   Rp?EK)  Rp?ii ; i)u´  Call ``submit(headers)`` with a fresh ``x-idempotency-key``; on a 429 whose Retry-After
fits the cap, wait it out (interrupt-aware) and resubmit ONCE under a new key.

A second 429, or one with an unknown/too-long Retry-After, raises ValueError naming the
rate limit â€” it must never fall through to the callers' "model may not be enabled" 4xx text,
which sends agents off to switch models. Every other exception propagates untouched.
)Úis_interruptedúx-idempotency-keyrO   Ng      à?g        )é   r   )Útools.interruptra   r!   ÚuuidÚuuid4r    r9   rU   Ú(MANAGED_FAL_RATE_LIMIT_RETRY_CAP_SECONDSr-   r]   ÚtimeÚ	monotonicÚsleepÚminÚmax)r_   r;   rW   ra   Úattemptr$   rX   Údeadlines   &$$     r   Ú(submit_managed_fal_with_rate_limit_retryro   “   sö   € õ /Ûˆð	LÙÐ.´´D·J²J³LÓ0AÐBÓCÒCó øô ô 
	LÜ# CÓ(¨CÔ/ØÜ:¸3Ó?ˆKØ˜!Œ|˜{Ò2°kÔDlÔ6lÜ Ô!@ÀÈ[Ó!YÓZÐ`cÐcÜ—~’~Ó'¨+Õ5ˆHÜ—.’.Ó" XÔ-Ù!×#Ò#Ü$Ô%DÀTÐQ\Ó%]Ó^ÐdgÐgÜ—
’
œ3˜s¤C¨¨X¼¿ºÓ8HÕ-HÓ$IÓJÖKö .ûð
	Lús   �%9¹D7ÁB
D2ÃAD2Ä2D7c               ó$   € V ^8„  d   QhRRRRRR/# )r   Úvaluer   r;   r!   r   r   )r   s   "r   r   r   ®   s!   € ÷ ñ �Cð ˜sð  sñ r   c                ó*   € V f   \        V R24      hV # )Nz) is required for managed FAL gateway mode)ÚRuntimeError)rq   r;   s   &&r   Ú_requirert   ®   s   € Ø‚}Ü˜d˜VÐ#LÐMÓNÐNØ€Lr   c                  óN   € ] tR t^´tRtR R ltRRRRRRR	RR
RRR/R R lltRtR# )Ú_ManagedFalSyncClientz¾Drives a Nous-managed fal-queue gateway via ``fal_client.SyncClient`` primitives; carries
its own ``fal_client`` reference so the caller decides which (possibly test-patched) module is used.c               ó$   € V ^8„  d   QhRRRRRR/# )r   r"   r   Úkeyr!   r'   r   )r   s   "r   r   Ú"_ManagedFalSyncClient.__annotate__¸   s&   € ÷ Vñ V 3ð V°ð VÀsñ Vr   c               	ó$  € \        \        VR R4      R4      p\        \        VRR4      R4      p\        V4      V n        V! VR7      V n        \        \        V P                  RR4      R4      V n        \        VRR4      V n        \        VR	R4      V n        V P                  e   V P                  f   \        R
4      h\        \        VRR4      R4      V n	        \        VRR4      V n
        \        VRR4      V n        \        VRR4      V n        R# )Ú
SyncClientNzfal_client.SyncClientÚclientzfal_client.client)rx   Ú_clientzfal_client.SyncClient._clientÚ_maybe_retry_requestÚ_raise_for_statuszKfal_client.client request helpers are required for managed FAL gateway modeÚSyncRequestHandlez#fal_client.client.SyncRequestHandleÚadd_hint_headerÚadd_priority_headerÚadd_timeout_header)rt   r5   r/   Ú_queue_url_formatÚ_sync_clientÚ_http_clientr~   r   rs   Ú_request_handle_classÚ_add_hint_headerÚ_add_priority_headerÚ_add_timeout_header)Úselfr"   rx   r'   Úsync_client_classÚclient_modules   &&$$  r   Ú__init__Ú_ManagedFalSyncClient.__init__¸   s  € Ü$¤W¨Z¸ÀtÓ%LÐNeÓfÐÜ ¤¨°X¸tÓ!DÐFYÓZˆÜ!@ÐAQÓ!RˆÔÙ-°#Ô6ˆÔÜ$¤W¨T×->Ñ->À	È4Ó%PÐRqÓrˆÔÜ$+¨MÐ;QÐSWÓ$XˆÔ!Ü!(¨Ð8KÈTÓ!RˆÔØ×$Ñ$Ò,°×0FÑ0FÒ0NÜÐlÓmÐmÜ%-Ü�MÐ#6¸Ó=Ð?dó&fˆÔ"ä '¨Ð7HÈ$Ó OˆÔÜ$+¨MÐ;PÐRVÓ$WˆÔ!Ü#*¨=Ð:NÐPTÓ#UˆÖ r   Úpathr)   r\   NÚwebhook_urlÚpriorityrP   Ústart_timeoutc               ó8   € V ^8„  d   QhRRRRRRRRRRR	R
RRRR/# )r   Úapplicationr!   Ú	argumentszdict[str, Any]r�   r\   r<   r‘   r’   r   rP   zOptional[dict[str, str]]r“   zOptional[int | float]r   )r   s   "r   r   ry   È   s\   € ÷ +dñ +dØð+dØ+9ð+dØDGð+dàð+dà1>ð+dàQTð+dð *ð+dð BWñ+dr   c               	óÄ  € V P                   V,           p	V'       d    V	R VP                  R 4      ,           ,          p	Ve   V	R\        RV/4      ,           ,          p	\        T;'       g    / 4      p
Ve    V P                  e   V P	                  WJ4       Ve+   V P
                  f   \        R4      hV P                  Wj4       Ve+   V P                  f   \        R4      hV P                  WŠ4       RVR\        V P                  RR4      R	V
/p\        ;QJ d    R
 V
 4       F  '       g   K   RM	  RM! R
 V
 4       4      pV'       d    V P                  P                  ! RV	3/ VB pMV P                  ! V P                  RV	3/ VB pV P                  V4       VP                  4       pV P!                  VR,          VR,          VR,          VR,          V P                  R7      # )r*   Ú?Úfal_webhookzGfal_client.client.add_priority_header is required for priority requestszEfal_client.client.add_timeout_header is required for timeout requestsrF   ÚtimeoutÚdefault_timeoutg      ^@rP   c              3  óZ   "  € T F!  p\        V4      P                  4       R 8H  x € K#  	  R# 5i)rb   N)r!   Úlower)Ú.0rx   s   & r   Ú	<genexpr>Ú/_ManagedFalSyncClient.submit.<locals>.<genexpr>è   s$   é € ð "
Ù?N¸ŒC�‹H�N‰NÓÐ 3Ö3»ùs   ‚)+TFÚPOSTÚ
request_idÚresponse_urlÚ
status_urlÚ
cancel_url)r¢   r£   r¤   r¥   r|   )r„   Úlstripr   rG   rˆ   r‰   rs   rŠ   r5   r…   Úanyr†   Úrequestr~   r   rF   r‡   )r‹   r•   r–   r�   r\   r‘   r’   rP   r“   ÚurlÚrequest_headersÚrequest_kwargsÚhas_idempotency_keyr3   Údatas   &&&$$$$$$      r   r_   Ú_ManagedFalSyncClient.submitÈ   sÆ  € ð
 ×$Ñ$ {Õ2ˆßØ�3˜Ÿ™ SÓ)Õ)Õ)ˆCØÒ"Ø�3œ M°;Ð#?Ó@Õ@Õ@ˆCÜ˜wŸ}˜}¨"Ó-ˆØÒ × 5Ñ 5Ò AØ×!Ñ! $Ô8ØÒØ×(Ñ(Ò0Ü"Ð#lÓmÐmØ×%Ñ% hÔ@ØÒ$Ø×'Ñ'Ò/Ü"Ð#jÓkÐkØ×$Ñ$ ]ÔDà�IØ”w˜t×0Ñ0Ð2CÀUÓKØ�ð
ˆ÷ "›cñ "
Ù?Nó"
ŸcŸcšcñ "
Ù?Nó"
ó 
Ð÷ Ø×(Ñ(×0Ò0°¸ÑOÀÑO‰Hà×0Ò0°×1BÑ1BÀFÈCÑbÐSaÑbˆHØ×Ñ˜xÔ(Ø�}‰}‹ˆØ×)Ñ)Ø˜LÕ)¸¸^Õ8LØ˜LÕ)°d¸<Õ6HÐQU×QbÑQbð *ó dð 	dr   )	rˆ   r‰   rŠ   r†   r~   r„   r   r‡   r…   )Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__rŽ   r_   Ú__static_attributes__r   r   r   rv   rv   ´   sV   † ñlõVð +dØJLð+dà"ð+dàAEð+dàW[ð+dð -1ð+dð Z^÷+dó +dr   rv   )r³   Ú
__future__r   rh   re   Útypingr   r   r   r   r   Úurllib.parser   rg   ÚFAL_QUEUE_POLL_SECONDSr   r%   r/   r9   rK   rU   r]   ro   rt   rv   r   r   r   Ú<module>r¹      sp   ðñõ4 #ã Û ß 7Õ 7Ý "ð ,0Ð (ð Ð ðÐ<R÷ õõ.#õ7õõ8õ(õLõ6÷?dó ?dr   