Ë
    ãß²j5  ã            
       ó®   — d Z ddlZ ej                  d«      ZdddddœZd	efd
„Zded	efd„Zd	efd„Z	dededede
d	ef
d„Zded	efd„Zdededed	efd„Zy)a�  One-shot keyless-ring rescue for failed keyed/configured web calls.

Stateless by design: a rescue routes THIS call through the free-tier ring (plugins/web/keyless_mcp.py);
the next web_search/web_extract call attempts the chosen backend again. Callers must never cache a
rescue-served response, or the one-shot rescue becomes sticky for a whole TTL. Logs under the origin
(tools.web_tools) logger.
é    Nztools.web_toolsÚEXA_API_KEYÚPARALLEL_API_KEYÚFIRECRAWL_API_KEYÚKEENABLE_API_KEY)ÚexaÚparallelÚ	firecrawlÚkeenableÚreturnc                  ó²   — ddl m}   | «       j                  dd«      sy	 ddlm}  |«       S # t
        $ r }t        j                  d|«       Y d}~yd}~ww xY w)	zV``web.keyless_rescue`` (default on), implicitly off when the keyless tier is disabled.r   )Ú_load_web_configÚkeyless_rescueTF)Ú_keyless_tier_enabledz$keyless rescue tier check failed: %sN)Útools.web_toolsr   ÚgetÚagent.web_search_registryr   Ú	ExceptionÚloggerÚdebug)r   r   Úexcs      ú;/home/ubuntu/.hermes/hermes-agent/tools/web_tools_rescue.pyÚ_keyless_rescue_enabledr      sO   € å0ÙÓ×!Ñ!Ð"2°DÔ9ØðÝCÙ$Ó&Ð&øÜò Ü�‰Ð;¸SÔAÜûðús    - ­	A¶AÁAÚnamec                 ó˜   — | dk(  rddl m}  |«       S ddlm} ddlm} t        j                  | d«      } || |r ||«      «      S d«      S )uÞ  Did *name*'s own provider route this call through the anonymous keyless ring?

    Mirrors the predicate each ring provider evaluates before calling, so eligibility reflects what
    actually happened. Firecrawl owns extra routes that bypass the ring without a key â€” the managed
    Nous Tool Gateway (persisted ``nous`` selection, or the legacy never-configured fallback when the
    gateway is ready) and a self-hosted ``FIRECRAWL_API_URL`` â€” so it is asked directly.
    r	   r   )Ú_use_keyless_ring)Úget_provider_env)Úuse_keylessÚ )Úplugins.web.firecrawl.providerr   Úagent.web_search_providerr   Úplugins.web.keyless_mcpr   Ú_RING_KEY_VARSr   )r   r   r   r   Úkey_vars        r   Ú_ring_vendor_keylessr$   !   sM   € ð ˆ{ÒÝDÙ Ó"Ð"Ý:Ý3Ü× Ñ   rÓ*€GÙ�t¹'Ñ-¨gÓ6ÓJÐJÀrÓJÐJó    c                 óÀ   — t        «       r| €y	 ddlm} t        | dd«      }||vxs t	        |«       S # t
        $ r }t        j                  d|«       Y d}~yd}~ww xY w)us  True when a failed call on *provider* should get a one-shot rescue.

    Eligible: any call that did NOT go through the keyless ring â€” a non-ring backend, a ring vendor
    in keyed mode, or a ring vendor routed through the managed gateway / a self-hosted instance. A
    ring vendor that walked the ring is NOT eligible: its failure means the ring already failed.
    NFr   )Ú_KEYLESS_RINGr   r   z#rescue eligibility check failed: %s)r   r!   r'   Úgetattrr$   r   r   r   )Úproviderr'   r   r   s       r   Ú_rescue_eligibler*   2   sc   € ô #Ô$¨Ð(8ØðÝ9Ü�x ¨Ó,ˆØ˜=Ð(ÒJÔ0DÀTÓ0JÐ,JÐJøÜò Ü�‰Ð:¸CÔ@Üûðús   �$4 ´	A½AÁAÚprovider_nameÚoriginal_errorÚqueryÚlimitc           
      ó*  — ddl m} t        j                  d| |xs ddd «        || ||«      }|j	                  d«      r6|j                  di «      j                  | d	| › d
|xs ddd › d| › d�¬«       |S d|xs d› d|j	                  dd«      › d�dœS )zSRescue a failed search via the ring; annotate the result with the original failure.r   )Úsearch_with_failoverz<web_search backend '%s' failed (%s); one-shot keyless rescuer   NéÈ   ÚsuccessÚdatazConfigured backend 'z' failed this call (zunknown erroré,  zC); result served by the keyless free tier. The next call will use 'z' again.)Úrescued_fromÚbackend_errorFzsearch failedz (keyless rescue also failed: ÚerrorÚunknownú))r2   r7   )r!   r0   r   Úwarningr   Ú
setdefaultÚupdate)r+   r,   r-   r.   r0   Úrescueds         r   Ú_rescue_searchr>   D   sÓ   € å<Ü
‡N�NØFØ˜Ò,¨"¨d¨sÐ3ôñ # =°%¸Ó?€GØ‡{�{�9ÔØ×Ñ˜6 2Ó&×-Ñ-Ø&à& } oð 6Ø#Ò6 ¸¸Ð=Ð>ð ?+Ø+8¨/¸ðCð 	.ô 	
ð ˆð àÒ0 Ð1ð 2,Ø,3¯K©K¸ÀÓ,KÐ+LÈAðOñð r%   Úresultc                 óš   — t        | j                  d«      xs d«      j                  «       }t        | j                  d«      «      xs d|v S )ub   True for a website-policy refusal â€” intentional, never rescued (it would fetch blocked content).r7   r   Úblocked_by_policyzblocked by website policy)Ústrr   ÚlowerÚbool)r?   r7   s     r   Ú_policy_blocked_resultrE   `   sD   € ä�—
‘
˜7Ó#Ò) rÓ*×0Ñ0Ó2€EÜ�—
‘
Ð.Ó/Ó0ÒXÐ4OÐSXÐ4XÐXr%   ÚurlsÚresultsc                 ó$  ‡— ddl m} t        ‰«      t        |«      k(  }t        ‰«      D ��cg c]  \  }}|rt	        |«      rŒ|‘Œ }}}|s‰S |r|D �cg c]  }||   ‘Œ	 c}n
t        |«      }ˆfd„|D «       }	t        |	d«      }
t        j                  d| t        |«      |
xs ddd «        || t        |«      «      }|rt        d	„ |D «       «      r‰S |D ]I  }|j                  d
«      rdn|j                  di «      }t        |t        «      sŒ9| |d<   |
xs ddd |d<   ŒK |rZt        |«      t        |«      k(  rCt        t        ||«      «      }t        ‰«      D ��cg c]  \  }}|j                  ||«      ‘Œ c}}S |S c c}}w c c}w c c}}w )a  Rescue a whole-batch extract failure via the ring.

    Only genuine failures are re-fetched; policy-blocked entries are preserved verbatim. If the provider
    broke url/result order parity, every entry is treated as rescueable and the ring's list replaces the
    batch wholesale.
    r   )Úextract_with_failoverc              3   óp   •K  — | ]-  }‰|   j                  d «      sŒ‰|   j                  d «      –— Œ/ y­w)r7   N©r   )Ú.0ÚirG   s     €r   ú	<genexpr>z"_rescue_extract.<locals>.<genexpr>u   s-   øè ø€ ÒU¨!¸WÀQ¹Z¿^¹^ÈGÕ=Tˆg�a‰j�n‰n˜W×%ÑUùs   ƒ6�6zextract failedzKweb_extract backend '%s' failed all %d URL(s) (%s); one-shot keyless rescuer   Nr1   c              3   ó@   K  — | ]  }|j                  d d«      –— Œ y­w)r7   r   NrK   )rL   Úrs     r   rN   z"_rescue_extract.<locals>.<genexpr>|   s   è ø€ Ò;¨a�q—u‘u˜W b×)Ñ;ùs   ‚r7   Úmetadatar5   r4   r6   )r!   rI   ÚlenÚ	enumeraterE   ÚlistÚnextr   r:   Úallr   r;   Ú
isinstanceÚdictÚzip)r+   rF   rG   rI   ÚparityrM   rP   Ú
rescue_idxÚrescue_urlsÚerrorsr,   r=   ÚmetaÚreplacementss     `           r   Ú_rescue_extractr`   f   s„  ø€ õ >ä�‹\œS ›YÑ&€FÜ )¨'Ó 2×b™˜˜1¹&ÔH^Ð_`ÕHa’!Ðb€JÑbÙØˆá39 JÖ/˜q�4˜“7Ó/¼tÀD»z€KÛU¨zÔU€FÜ˜&Ð"2Ó3€NÜ
‡N�NØUØ”s˜;Ó'¨.Ò*>¸BÀÀÐ)Eôñ $ M´4¸Ó3DÓE€GÙ”3Ñ;°7Ô;Ô;ØˆØò AˆØ—u‘u˜W”~‰t¨1¯<©<¸
ÀBÓ+GˆÜ�dœDÕ!Ø#0ˆD�Ñ Ø%3Ò%9°r¸4¸CÐ$@ˆD�Ò!ð	Añ
 ”#�g“,¤# j£/Ò1ÜœC 
¨GÓ4Ó5ˆÜ3<¸WÓ3E×F©4¨1¨a�× Ñ   AÕ&ÓFÐFØ€Nùó- cùò 0ùó" Gs   ­FÁFÁFÅF)Ú__doc__ÚloggingÚ	getLoggerr   r"   rD   r   rB   r$   r*   ÚintrX   r>   rE   rT   r`   © r%   r   ú<module>rf      sÅ   ðñó à	ˆ×	Ñ	Ð,Ó	-€ð Ð&8Ø$Ð2Dñ€ð
 ó 
ðK˜sð K tó Kð" $ó ð$ #ð °sð À3ð Èsð ÐW[ó ð8Y 4ð Y¨Dó Yð  3ð  ¨dð  ¸Tð  Àdô  r%   