Ë
    áß²jð%  ã                  ó4  — U d Z ddlmZ ddlZddlmZ ddlmZmZ ddl	m
Z
  ej                  e«      Z ede
e¬«      Zd	ed
<   ej!                   e«       «       dd„Zdd„ZdZdZdd„Zdd„Zdd„Zd ddœd!d„Zd"d„Zd"d„ZddlmZ ddlmZ ddlZddiZd„ Z y)#u‚  Web Search Provider Registry.

Populated by plugins via :meth:`PluginContext.register_web_search_provider`;
consumed by the ``web_search`` / ``web_extract`` wrappers in :mod:`tools.web_tools`.

Active selection, in precedence order (the ``supports_search`` /
``supports_extract`` capability filter applies at every step, so a search-only
provider configured as ``web.extract_backend`` falls through):

1. ``web.search_backend`` / ``web.extract_backend``, then ``web.backend``.
2. The single capability-eligible provider that is registered AND available.
3. Legacy preference walk (``_LEGACY_PREFERENCE``) filtered by availability â€”
   the historic ``tools.web_tools._get_backend()`` order, so installs that never
   set a config key keep landing on the same provider.
4. Keyless free-tier walk (``_KEYLESS_PREFERENCE``), last resort.
5. ``None`` â€” the tool points the user at ``hermes tools``.
é    )ÚannotationsN)ÚOptional)ÚProviderRegistryÚis_available_safe)ÚWebSearchProviderÚWeb)ÚlabelÚprovider_clsÚloggerz#ProviderRegistry[WebSearchProvider]Ú	_registryc                 óL  — 	 ddl m}  |«       }| D ]%  }t        |t        «      s y|j	                  |«      }Œ' t        |t
        «      r |j                  «       r|j                  «       S y# t        $ r0}t        j                  ddj                  | «      |«       Y d}~yd}~ww xY w)zGResolve a dotted config key from ``config.yaml``. Returns None on miss.r   )Úload_config_readonlyNzCould not read config %s: %sú.)Úhermes_cli.configr   Ú
isinstanceÚdictÚgetÚstrÚstripÚ	Exceptionr   ÚdebugÚjoin)Úpathr   ÚcurÚsegmentÚexcs        ú>/home/ubuntu/.hermes/hermes-agent/agent/web_search_registry.pyÚ_read_config_keyr   $   s•   € ðJÝ:á"Ó$ˆØò 	#ˆGÜ˜c¤4Ô(ÙØ—'‘'˜'Ó"‰Cð	#ô �cœ3Ô C§I¡I¤KØ—9‘9“;Ðð øô ò JÜ�‰Ð3°S·X±X¸d³^ÀS×IÐIØûðJús   ‚#A* ¦AA* Á*	B#Á3&BÂB#c                ó<   — t        d| › d�«      xs t        dd«      S )zN``web.<capability>_backend`` (preferred) or ``web.backend`` (shared fallback).ÚwebÚ_backendÚbackend)r   ©Ú
capabilitys    r   Ú_configured_backendr%   5   s%   € ä˜E j \°Ð#:Ó;ÒaÔ?OÐPUÐW`Ó?aÐaó    )Ú	firecrawlÚparallelÚtavilyÚ
perplexityÚexaÚsearxngz
brave-freeÚddgs)r+   r(   r'   Úkeenablec                 óº   — 	 ddl m} m} |t        | «      z  }t	        | |d | d| z   «      S # t
        $ r%}t        j                  d|«       Y d}~t        S d}~ww xY w)zÄKeyless walk order, starting at the ring cursor in
    :mod:`plugins.web.keyless_mcp` so resolution and dispatch agree on which
    vendor a fresh install starts at; the rest follow in ring order.r   )Ú_KEYLESS_RINGÚ_ring_cursorNz"keyless ring order unavailable: %s)	Úplugins.web.keyless_mcpr0   r1   ÚlenÚtupler   r   r   Ú_KEYLESS_PREFERENCE)r0   r1   Ústartr   s       r   Ú_keyless_preferencer7   E   sb   € ð@ßGàœs =Ó1Ñ1ˆÜ�] 5 6Ð*¨]¸6¸EÐ-BÑBÓCÐCøÜò @Ü�‰Ð9¸3×?Ð?ÜÐûð@ús   ‚), ¬	AµAÁAc               óª  ‡— t         j                  «       }d	ˆfd„}d	d„}| rM|j                  | «      }|�
 ||«      r|S |€t        j	                  d| «       nt        j	                  d| ‰«       |j                  «       D �cg c]  } ||«      sŒ ||«      sŒ|‘Œ }}t        |«      dk(  r|d   S t        D ]  }|j                  |«      }|€Œ||v sŒ|c S  t        «       r@t        «       D ]3  }	|j                  |	«      }|� ||«      sŒ	 |j                  «       r|c S Œ5 yc c}w # t        $ r!}
t        j	                  d|	|
«       Y d}
~
Œad}
~
ww xY w)
a¢  Resolve the active provider for a capability ("search" | "extract").

    Rules, in order (see module docstring): explicit config wins even when
    ``is_available()`` is False (the dispatcher surfaces a precise
    "X_API_KEY is not set" error instead of a silent switch); then the single
    available capable provider; then the availability-filtered legacy walk;
    then the keyless free-tier walk; else None.
    c                ó~   •— t        ‰dk(  r| j                  «       «      S ‰dk(  r| j                  «       «      S d«      S )NÚsearchÚextractF)ÚboolÚsupports_searchÚsupports_extract)Úpr$   s    €r   Ú_capablez_resolve.<locals>._capable^   sM   ø€ ÜØ#-°Ò#9ˆA×ÑÓó
ð 	
à)3°yÒ)@�×#Ñ#Ó%ó
ð 	
àFKó
ð 	
r&   c                ó$   — t        | t        d«      S )Nz$provider %s.is_available() raised %s)r   r   )r?   s    r   Ú
_availablez_resolve.<locals>._availabled   s   € Ü  ¤FÐ,RÓSÐSr&   Nz<web backend '%s' configured but not registered; falling backzCweb backend '%s' configured but does not support '%s'; falling backé   r   z,provider %s.is_keyless_available() raised %s)r?   r   Úreturnr<   )r   Úmergedr   r   r   Úvaluesr3   Ú_LEGACY_PREFERENCEÚ_keyless_tier_enabledr7   Úis_keyless_availabler   )Ú
configuredr$   Úsnapshotr@   rB   Úproviderr?   ÚeligibleÚlegacyÚnamer   s    `         r   Ú_resolverP   S   se  ø€ ô ×ÑÓ!€Hõ
óTñ Ø—<‘< 
Ó+ˆØÐ¡H¨XÔ$6ØˆOØÐÜ�L‰LÐWÐYcÕdä�L‰LØUÐWaÐcmôð $Ÿ?™?Ó,ÖN�a±¸µÁ
È1Å’ÐN€HÐNÜ
ˆ8ƒ}˜ÒØ˜‰{Ðä$ò ˆØ—<‘< Ó'ˆØÑ H°Ò$8ØŠOðô ÔÜ'Ó)ò 	XˆDØ—|‘| DÓ)ˆHØÐ¡x°Ô'9ØðXØ×0Ñ0Ô2Ø#’Oð 3ð	Xð ùò/ Oøô( ò XÜ—‘ÐKÈTÐSV×WÑWûðXús*   ÂD#ÂD#ÂD#ÄD(Ä(	EÄ1EÅEc                 óÒ   — 	 ddl m}   | «       j                  d«      xs i }t        |j                  dd«      «      S # t        $ r }t
        j                  d|«       Y d}~yd}~ww xY w)zBRead ``web.keyless_fallback`` from config.yaml (default: enabled).r   )Úload_configr    Úkeyless_fallbackTz'keyless_fallback config read failed: %sN)r   rR   r   r<   r   r   r   )rR   Úweb_cfgr   s      r   rH   rH   Ž   sZ   € ðÝ1á“-×#Ñ# EÓ*Ò0¨bˆÜ�G—K‘KÐ 2°DÓ9Ó:Ð:øÜò Ü�‰Ð>ÀÔDÜûðús   ‚:= ½	A&ÁA!Á!A&r#   c               óÂ  — dd„}| s|dv rt        |«      } | sy || «      }	 ddlm}  |«       }|j                  j	                  «       D ]g  \  }}t        |t        «      sŒ|j                  d«      sŒ)|j                  rŒ6|j                  dk(  sŒF ||j                  dd	«      d	   «      |k(  sŒe|c S  	 y# t        $ r }t        j                  d
|«       Y d}~yd}~ww xY w)u7  Plugin key of a *disabled* bundled web plugin that would have provided
    the configured backend (``web.<capability>_backend`` â†’ ``web.backend``), or None.

    Lets the dispatcher say "re-enable web-firecrawl" instead of "No web extract
    provider configured". Resolved from config.yaml rather than the resolved
    backend because a disabled provider fails the availability gate and silently
    drops to the default. Bundled web plugins live under ``web/<vendor>`` with
    the provider name differing only by hyphen/underscore, so both are normalized.

    When a user sets ``web.extract_backend: firecrawl`` (or the search equivalent) but also lists
    ``web-firecrawl`` in ``plugins.disabled``, the provider never registers and the dispatcher would
    otherwise emit a misleading "No web extract provider configured. Set web.extract_backend to ..." error â€”
    even though the backend IS configured correctly. This helper detects that case so the dispatcher can
    point the user at the actual cause (issue #40190 follow-up: pi314's disabled-plugin symptom).
    c                ó^   — | j                  «       j                  «       j                  dd«      S )NÚ-Ú_)r   ÚlowerÚreplace)Úss    r   Ú_normz'_disabled_web_plugin_for.<locals>._normª   s"   € Ø�w‰w‹y�‰Ó ×(Ñ(¨¨cÓ2Ð2r&   )r:   r;   Nr   )Úget_plugin_managerzweb/zdisabled via configú/rC   z%disabled-web-plugin lookup failed: %s)r[   r   rD   r   )r%   Úhermes_cli.pluginsr]   Ú_pluginsÚitemsr   r   Ú
startswithÚenabledÚerrorÚsplitr   r   r   )	rJ   r$   r\   Úwantr]   ÚpmÚkeyÚloadedr   s	            r   Ú_disabled_web_plugin_forrj   š   sÙ   € ó 3ñ ˜*Ð(=Ñ=Ü(¨Ó4ˆ
ÙØá�Ó€DðCÝ9áÓ!ˆØŸ;™;×,Ñ,Ó.ò 	‰KˆC�ä˜3¤Õ$¨¯©¸Õ)?ÈÏËØ—L‘LÐ$9Ó9¹eÀCÇIÁIÈcÐSTÓDUÐVWÑDXÓ>YÐ]aÓ>aà’
ñ	ð øô ò CÜ�‰Ð<¸c×BÐBØûðCús;   ¢=B5 Á B5 Á2B5 Á?B5 ÂB5 Â.B5 Â2B5 Â5	CÂ>CÃCc                 ó.   — t        t        d«      d¬«      S )z1Resolve the currently-active web search provider.r:   r#   ©rP   r%   © r&   r   Úget_active_search_providerrn   Â   s   € äÔ'¨Ó1¸hÔGÐGr&   c                 ó.   — t        t        d«      d¬«      S )z2Resolve the currently-active web extract provider.r;   r#   rl   rm   r&   r   Úget_active_extract_providerrp   Ç   s   € äÔ'¨	Ó2¸yÔIÐIr&   )ÚDict)ÚListÚhermes_home_key)Úhermes_constantsrs   c                óÌ   — t         j                  | «      }|€t        dt        ›d| ›�«      ‚dd l}ddlm}  |t        | g|¢­Ž  t        |j                  |d   «      |d   «      S )Nzmodule z has no attribute r   )Ú	warn_oncerC   )	Ú_PLUGIN_COMPAT_LAZYr   ÚAttributeErrorÚ__name__Ú	importlibÚhermes_cli.plugin_compatrv   ÚgetattrÚimport_module)rO   Útargetrz   rv   s       r   Ú__getattr__r   Ú   se   € Ü ×$Ñ$ TÓ*€FØ€~Ü˜w¤x lÐ2DÀTÀHÐMÓNÐNÛÝ2ÙŒh˜Ð&˜vÓ&Ü�9×*Ñ*¨6°!©9Ó5°v¸a±yÓAÐAr&   )r   r   rD   úOptional[str])r$   r   rD   r€   )rD   r4   )rJ   r€   r$   r   rD   úOptional[WebSearchProvider])rD   r<   )N)rJ   r€   r$   r€   rD   r€   )rD   r�   )!Ú__doc__Ú
__future__r   ÚloggingÚtypingr   Úagent.provider_registryr   r   Úagent.web_search_providerr   Ú	getLoggerry   r   r   Ú__annotations__ÚexportÚglobalsr   r%   rG   r5   r7   rP   rH   rj   rn   rp   rq   rr   Ú	threadingrw   r   rm   r&   r   ú<module>r�      sÄ   ðòõ$ #ã Ý ç GÝ 7à	ˆ×	Ñ	˜8Ó	$€ñ 2BØ
Ð/¸ô2€	Ð.ó ð 
× Ñ ‘“Ô óó"bð oÐ ð CÐ óó8óv	ð%Ð^bõ %óPHó
Jõ Ý Û ð Ð>ðÐ ó
Br&   