Ë
    ãß²jý(  ã                  ó°   — d Z ddlmZ ddlZddlZddlmZmZmZm	Z	m
Z
 ddlmZ dZdd„Zdd„Zdd	„Zdd
„Zdd„Zdd„Z	 	 	 	 	 	 dd„Zdd„Z G d„ d«      Zy)aº  Shared FAL.ai SDK plumbing: lazy import, managed-gateway sync client, small helpers.

Stateful pieces (cache globals, ``_managed_fal_client*``, ``_submit_fal_request``)
intentionally stay on :mod:`tools.image_generation_tool`: it is the patch target for the
test suites and for ``plugins/image_gen/fal/``'s ``_it`` indirection, so moving the caches
here would silently defeat ``monkeypatch.setattr(image_tool, "_managed_fal_client", None)``.
é    )ÚannotationsN)ÚAnyÚCallableÚDictÚOptionalÚUnion)Ú	urlencodeg      >@c                 óŽ   — 	 ddl m}   | dd¬«       ddl}|S # t        $ r Y Œt        $ r}t        t	        |«      «      ‚d}~ww xY w)z·Import ``fal_client`` (via ``lazy_deps`` when available); raises ImportError if unavailable.

    Callers cache the result on their own module global so tests can monkeypatch it.
    r   )Úensurez	image.falF)ÚpromptN)Útools.lazy_depsr   ÚImportErrorÚ	ExceptionÚstrÚ
fal_client)Ú_lazy_ensureÚexcr   s      ú5/home/ubuntu/.hermes/hermes-agent/tools/fal_common.pyÚimport_fal_clientr      sK   € ð
$Ý:Ù�[¨Õ/ó
 ØÐøô ò ÙÜò $Üœ#˜c›(Ó#Ð#ûð$ús   ‚ ˜	A£A«?¿Ac                ó~   — t        | xs d«      j                  «       j                  d«      }|st        d«      ‚|› d�S )NÚ ú/z$Managed FAL queue origin is required)r   ÚstripÚrstripÚ
ValueError)Úqueue_run_originÚnormalized_origins     r   Ú_normalize_fal_queue_url_formatr   $   sD   € ÜÐ,Ò2°Ó3×9Ñ9Ó;×BÑBÀ3ÓGÐÙÜÐ?Ó@Ð@ØÐ  Ð"Ð"ó    c                ó    — t        | dd«      }|�t        |dd«      }t        |t        «      r|S t        | dd«      }t        |t        «      r|S dS )zjHTTP status from httpx (``.response.status_code``) or fal_client (``.status_code``) exceptions, else None.ÚresponseNÚstatus_code)ÚgetattrÚ
isinstanceÚint)r   r!   Ústatuss      r   Ú_extract_http_statusr'   +   sU   € ä�s˜J¨Ó-€HØÐÜ˜ =°$Ó7ˆÜ�fœcÔ"ØˆMÜ�S˜-¨Ó.€FÜ ¬Ô,ˆ6Ð6°$Ð6r   c                óv  — t        | dd«      }|€y	 |j                  «       }t        |t        «      r|j                  d«      nd}t        |t        «      r|j                  d«      dk7  ryt        |j                  d«      t        «      r|j                  d«      ni }t        |j                  d«      t        «      r|j                  d«      ni }|j                  d«      xs |j                  d«      xs d	}|j                  d«      xs d
}|j                  d«      xs d› d|› d|› d|› d�S # t        $ r Y yw xY w)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.
    r!   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.)r#   Újsonr   r$   ÚdictÚget)	r   Úwhatr!   Úpayloadr)   r,   Úupstreamr*   Údetails	            r   Ú_managed_fal_billing_errorr8   6   s7  € ô �s˜J¨Ó-€HØÐØðØ—-‘-“/ˆô %/¨w¼Ô$=ˆG�K‰K˜Ô À4€EÜ�eœTÔ" e§i¡i°Ó&7¸?Ò&JØÜ&0°·±¸9Ó1EÄtÔ&Lˆe�i‰i˜	Ô"ÐRT€GÜ1;¸G¿K¹KÐHYÓ<ZÔ\`Ô1aˆw�{‰{Ð,Ô-Ðgi€HØ�<‰<˜ÓÒZ 7§;¡;Ð/FÓ#GÒZÈ?€DØ�\‰\˜'Ó"ÒUÐ&U€Fà�9‰9�YÓÒ@Ð#@Ð
AÐARÐSWÐRXÐXZÐ[aÐZbð c-à-1¨Fð 32ð	2ðøô ò Ùðús   ’D, Ä,	D8Ä7D8c                óŒ  — t        | dd«      }|�t        | «      dk7  ryt        |dd«      }|�t        |d«      r|j                  d«      nd}|€C	 |j	                  «       j                  d«      }t        |t        «      r|j                  d«      nd}	 |�t        |«      S dS # t
        $ r Y yw xY w# t        t        f$ r Y yw xY w)	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.r!   Né­  Úheadersr3   zRetry-Afterr)   Ú
retryAfter)r#   r'   Úhasattrr3   r1   r   r$   r2   ÚfloatÚ	TypeErrorr   )r   r!   r;   Úrawr)   s        r   Ú _managed_fal_retry_after_secondsrA   R   sÕ   € ô �s˜J¨Ó-€HØÐÔ/°Ó4¸Ò;ØÜ�h 	¨4Ó0€GØ(/Ð(;ÄÈÐQVÔ@Wˆ'�+‰+�mÔ
$Ð]a€CØ
€{ð	Ø—M‘M“O×'Ñ'¨Ó0ˆEô *4°E¼4Ô)@ˆe�i‰i˜Ô%ÀdˆðØ ˜_Œu�S‹zÐ6°$Ð6øô	 ò 	Ùð	ûô
 ”zÐ"ò Ùðús*   ÁB" ÂB1 Â B1 Â"	B.Â-B.Â1CÃCc                ó0   — |�d|d›d�nd}d| › d|› d|› d�S )	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.© )r4   ÚnameÚretry_afterÚhints       r   Ú_managed_fal_rate_limit_messagerI   f   s?   € Ø.9Ð.Eˆ\˜+ a˜¨Ñ*ÐKa€Dà
1°$°°r¸$¸¸}ÈTÈFð S_ð 	_ðr   c               ó`  — ddl m} dD ])  }	  | dt        t        j                  «       «      i«      c S  y# t
        $ rî}t        |«      dk7  r‚ t        |«      }|dk(  s|�	|t        kD  rt        t        |||«      «      |‚t        j                  «       |z   }t        j                  «       |k  rt |«       rt        t        |||«      «      |‚t        j                  t        dt        d	|t        j                  «       z
  «      «      «       t        j                  «       |k  rŒtY d}~�Œd}~ww xY w)
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.
    r   )Úis_interrupted)é   é   úx-idempotency-keyr:   rM   Ng      à?g        )Útools.interruptrK   r   ÚuuidÚuuid4r   r'   rA   Ú(MANAGED_FAL_RATE_LIMIT_RETRY_CAP_SECONDSr   rI   ÚtimeÚ	monotonicÚsleepÚminÚmax)Úsubmitr4   rF   rK   Úattemptr   rG   Údeadlines           r   Ú(submit_managed_fal_with_rate_limit_retryr[   n   s  € õ /Øò Lˆð	LÙÐ.´´D·J±J³LÓ0AÐBÓCÒCñLøô ò 
	LÜ# CÓ(¨CÒ/ØÜ:¸3Ó?ˆKØ˜!Š|˜{Ð2°kÔDlÒ6lÜ Ô!@ÀÀtÈ[Ó!YÓZÐ`cÐcÜ—~‘~Ó'¨+Ñ5ˆHÜ—.‘.Ó" XÒ-Ù!Ô#Ü$Ô%DÀTÈ4ÐQ\Ó%]Ó^ÐdgÐgÜ—
‘
œ3˜s¤C¨¨X¼¿¹Ó8HÑ-HÓ$IÓJÔKô —.‘.Ó" XÓ-ÿùð
	Lús   �$6¶	D-¿C"D(Ä(D-c                ó&   — | €t        |› d�«      ‚| S )Nz) is required for managed FAL gateway mode)ÚRuntimeError)Úvaluer4   s     r   Ú_requirer_   ‰   s   € Ø€}Ü˜d˜VÐ#LÐMÓNÐNØ€Lr   c                  óN   — e Zd ZdZdd„Zdddddddœ	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dd„Zy)	Ú_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               ó  — t        t        |dd «      d«      }t        t        |dd «      d«      }t        |«      | _         ||¬«      | _        t        t        | j                  dd «      d«      | _        t        |dd «      | _        t        |d	d «      | _        | j                  �| j                  €t        d
«      ‚t        t        |dd «      d«      | _	        t        |dd «      | _
        t        |dd «      | _        t        |dd «      | _        y )NÚ
SyncClientzfal_client.SyncClientÚclientzfal_client.client)ÚkeyÚ_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)r_   r#   r   Ú_queue_url_formatÚ_sync_clientÚ_http_clientrg   rh   r]   Ú_request_handle_classÚ_add_hint_headerÚ_add_priority_headerÚ_add_timeout_header)Úselfr   re   r   Úsync_client_classÚclient_modules         r   Ú__init__z_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   r   N)ÚpathrH   Úwebhook_urlÚpriorityr;   Ústart_timeoutc               óú  — | j                   |z   }	|r|	d|j                  d«      z   z  }	|�|	dt        d|i«      z   z  }	t        |xs i «      }
|�| j                  �| j	                  ||
«       |�)| j
                  €t        d«      ‚| j                  ||
«       |�)| j                  €t        d«      ‚| j                  ||
«       |t        | j                  dd«      |
dœ}t        d	„ |
D «       «      }|r | j                  j                  d
|	fi |¤Ž}n | j                  | j                  d
|	fi |¤Ž}| j                  |«       |j                  «       }| j!                  |d   |d   |d   |d   | j                  ¬«      S )Nr   ú?Úfal_webhookzGfal_client.client.add_priority_header is required for priority requestszEfal_client.client.add_timeout_header is required for timeout requestsÚdefault_timeoutg      ^@)r1   Útimeoutr;   c              3  óT   K  — | ]   }t        |«      j                  «       d k(  –— Œ" y­w)rN   N)r   Úlower)Ú.0re   s     r   ú	<genexpr>z/_ManagedFalSyncClient.submit.<locals>.<genexpr>Ã   s&   è ø€ ò "
Ø8;ŒC�‹H�N‰NÓÐ 3Õ3ñ"
ùs   ‚&(ÚPOSTÚ
request_idÚresponse_urlÚ
status_urlÚ
cancel_url)r†   r‡   rˆ   r‰   rd   )rm   Úlstripr	   r2   rq   rr   r]   rs   r#   rn   Úanyro   Úrequestrg   rh   r1   rp   )rt   ÚapplicationÚ	argumentsrx   rH   ry   rz   r;   r{   ÚurlÚrequest_headersÚrequest_kwargsÚhas_idempotency_keyr!   Údatas                  r   rX   z_ManagedFalSyncClient.submit£   s³  € ð
 ×$Ñ$ {Ñ2ˆÙØ�3˜Ÿ™ SÓ)Ñ)Ñ)ˆCØÐ"Ø�3œ M°;Ð#?Ó@Ñ@Ñ@ˆCÜ˜wš}¨"Ó-ˆØÐ × 5Ñ 5Ð AØ×!Ñ! $¨Ô8ØÐØ×(Ñ(Ð0Ü"Ð#lÓmÐmØ×%Ñ% h°Ô@ØÐ$Ø×'Ñ'Ð/Ü"Ð#jÓkÐkØ×$Ñ$ ]°OÔDàÜ˜t×0Ñ0Ð2CÀUÓKØ&ñ
ˆô "ñ "
Ø?Nô"
ó 
Ðñ Ø0�t×(Ñ(×0Ñ0°¸ÑOÀÑO‰Hà0�t×0Ñ0°×1BÑ1BÀFÈCÑbÐSaÑbˆHØ×Ñ˜xÔ(Ø�}‰}‹ˆØ×)Ñ)Ø˜LÑ)¸¸^Ñ8LØ˜LÑ)°d¸<Ñ6HÐQU×QbÑQbð *ó dð 	dr   )r   r   re   r   r   r   )r�   r   rŽ   zDict[str, Any]rx   r   rH   úOptional[str]ry   r”   rz   r   r;   zOptional[Dict[str, str]]r{   zOptional[Union[int, float]])Ú__name__Ú
__module__Ú__qualname__Ú__doc__rw   rX   rE   r   r   ra   ra   �   sw   „ ñlóVð" KMØ"ÀÐW[Ø,0Ð_cñ+dØð+dØ+9ð+dØDGð+dàð+dà1>ð+dàQTð+dð *ð+dð B]ô+dr   ra   )Úreturnr   )r   r   r™   r   )r   ÚBaseExceptionr™   zOptional[int])r   rš   r4   r   r™   r”   )r   rš   r™   úOptional[float])r4   r   rF   r   rG   r›   r™   r   )rX   zCallable[[Dict[str, str]], Any]r4   r   rF   r   )r^   r   r4   r   r™   r   )r˜   Ú
__future__r   rS   rP   Útypingr   r   r   r   r   Úurllib.parser	   rR   r   r   r'   r8   rA   rI   r[   r_   ra   rE   r   r   ú<module>rŸ      sv   ðñõ #ã Û ß 7Õ 7Ý "ð ,0Ð (óó #ó7óó8ó(ðLØ+ðLØ69ðLØADóLó6÷?dò ?dr   