
    PL
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 ddl	m
Z
  ej        e          ZddZdd
Z G d de
          ZdS )u:  Exa web search + content extraction — plugin form.

Subclasses :class:`agent.web_search_provider.WebSearchProvider`. Uses the
official Exa SDK (``exa-py``) which is lazy-loaded via
:func:`tools.lazy_deps.ensure` so that cold-start CLI users don't pay the
SDK import cost when Exa isn't configured.

Config keys this provider responds to::

    web:
      search_backend: "exa"      # explicit per-capability
      extract_backend: "exa"     # explicit per-capability
      backend: "exa"             # shared fallback for both

Env var::

    EXA_API_KEY=...    # https://exa.ai (paid tier; free trial available)

The previous in-tree implementation lived at
``tools.web_tools._exa_search`` / ``_exa_extract``; this file is the
canonical replacement. Behavior is bit-for-bit identical aside from the
ABC method-name change.
    )annotationsN)AnyDictList)WebSearchProviderreturnr   c                 h   ddl m}  t          | dd          }||S t          j        d          }|st          d          	 ddlm}  |dd	           n9# t          $ r Y n-t          $ r!}t          t          |                    d}~ww xY wdd
lm}  ||          }d|j        d<   || _        |S )zLazy-import and cache an Exa SDK client.

    Cache lives on :mod:`tools.web_tools` (as ``_exa_client``) so unit
    tests that reset that name between cases keep working. Raises
    ``ValueError`` when ``EXA_API_KEY`` is unset.
    r   N_exa_clientEXA_API_KEYzLEXA_API_KEY environment variable not set. Get your API key at https://exa.ai)ensurez
search.exaF)prompt)Exa)api_keyzhermes-agentzx-exa-integration)tools.web_tools	web_toolsgetattrosgetenv
ValueErrortools.lazy_depsr   ImportError	Exceptionstrexa_pyr   headersr
   )_wtcachedr   _lazy_ensureexcr   clients          </home/kuhnn/.hermes/hermes-agent/plugins/web/exa/provider.py_get_exa_clientr"   )   s    "!!!!!S-..Fi&&G 
1
 
 	

$::::::\%00000    $ $ $#c((###$ S!!!F*8FN&'COMs   A 
B"	B+BBNonec                      ddl m}  d| _        dS )z?Drop the cached Exa client so tests can re-instantiate cleanly.r   N)r   r   r
   )r   s    r!   _reset_client_for_testsr%   N   s    !!!!!!COOO    c                  t    e Zd ZdZedd            Zedd            ZddZddZdd	Z	dddZ
ddZddZdS )ExaWebSearchProvideru   Exa search + extract provider.

    Both methods are sync — Exa's SDK is sync-only. The web_extract_tool
    dispatcher wraps sync extracts via ``asyncio.to_thread`` when it
    needs to keep the event loop responsive.
    r   r   c                    dS )Nexa selfs    r!   namezExaWebSearchProvider.name]       ur&   c                    dS )Nr   r+   r,   s    r!   display_namez!ExaWebSearchProvider.display_namea   r/   r&   boolc                j    t          t          j        dd                                                    S )z=Return True when ``EXA_API_KEY`` is set to a non-empty value.r    )r2   r   r   stripr,   s    r!   is_availablez!ExaWebSearchProvider.is_availablee   s(    BImR006688999r&   c                    dS NTr+   r,   s    r!   supports_searchz$ExaWebSearchProvider.supports_searchi       tr&   c                    dS r8   r+   r,   s    r!   supports_extractz%ExaWebSearchProvider.supports_extractl   r:   r&      querylimitintDict[str, Any]c                   	 ddl m}  |            rdddS t                              d||           t	                                          ||ddi	          }g }t          |j        pg           D ]P\  }}|j        pg }|	                    |j
        pd
|j        pd
|rd                    |          nd
|dz   d           Qdd|idS # t          $ r}	dt          |	          dcY d}	~	S d}	~	wt          $ r}	dd|	 dcY d}	~	S d}	~	wt           $ r-}	t                              d|	           dd|	 dcY d}	~	S d}	~	ww xY w)zExecute an Exa search.

        Returns ``{"success": True, "data": {"web": [{...}, ...]}}`` on
        success, ``{"success": False, "error": str}`` on failure (incl.
        missing API key and SDK install errors).
        r   is_interruptedFInterrupted)successerrorzExa search: '%s' (limit=%d)
highlightsT)num_resultscontentsr4       )urltitledescriptionpositionweb)rF   dataNExa SDK not installed: zExa search error: %szExa search failed: )tools.interruptrD   loggerinfor"   search	enumerateresultsrH   appendrM   rN   joinr   r   r   r   warning)
r-   r>   r?   rD   responseweb_resultsiresultrH   r   s
             r!   rW   zExaWebSearchProvider.searcho   s   !	L666666~ B#(=AAAKK5ueDDD&((//!&- 0  H K&x'7'=2>> 	 		6#.4"
""%z/R!'!3?I'Qsxx
';';';r$%E	      $e[-ABBB 	9 	9 	9$s3xx88888888 	P 	P 	P$/N/N/NOOOOOOOO 	L 	L 	LNN13777$/JS/J/JKKKKKKKK	LsG   C	 B1C	 	
D>C*$D>*D>7D>D>D>"D93D>9D>urls	List[str]kwargsr   List[Dict[str, Any]]c           
     f  
 	 ddl m}  |            rd |D             S t                              dt	          |                     t                                          |d          }g }|j        pg D ];}|j        pd}|j	        pd}|j
        pd}	|                    ||	||||	dd	           <|S # t          $ r

fd
|D             cY d

S d

wt          $ r

fd|D             cY d

S d

wt          $ r3
t                              d
           
fd|D             cY d

S d

ww xY w)zExtract content from one or more URLs via Exa.

        Returns a list of result dicts shaped for the legacy LLM
        post-processing pipeline. On per-URL or whole-batch failure,
        results carry an ``error`` field rather than raising.
        r   rC   c                    g | ]}|d dd	S )rE   r4   )rM   rG   rN   r+   ).0us     r!   
<listcomp>z0ExaWebSearchProvider.extract.<locals>.<listcomp>   s/       HIACC  r&   zExa extract: %d URL(s)T)textr4   )	sourceURLrN   )rM   rN   contentraw_contentmetadatac                8    g | ]}|d d t                    dS )r4   rM   rN   rl   rG   )r   rg   rh   r   s     r!   ri   z0ExaWebSearchProvider.extract.<locals>.<listcomp>   s,    \\\RSArCHHMM\\\r&   Nc                $    g | ]}|d d d dS )r4   rS   rp   r+   rq   s     r!   ri   z0ExaWebSearchProvider.extract.<locals>.<listcomp>   s?        B2@_Z]@_@_``  r&   zExa extract error: %sc                $    g | ]}|d d d dS )r4   zExa extract failed: rp   r+   rq   s     r!   ri   z0ExaWebSearchProvider.extract.<locals>.<listcomp>   s?        B2@\WZ@\@\]]  r&   )rT   rD   rU   rV   lenr"   get_contentsrY   rj   rM   rN   rZ   r   r   r   r\   )r-   ra   rc   rD   r]   rY   r`   rl   rM   rN   r   s             @r!   extractzExaWebSearchProvider.extract   s    &	666666~  MQ    KK0#d))<<<&((55d5FFH,.G"*0b   ++j&B*"!&#*'.25$F$F     N 	] 	] 	]\\\\W[\\\\\\\\\ 	 	 	             	 	 	NN2C888           	sG   B3 BB3 3
D0=C
D0D0C0*D00D0=(D+%D0+D0c                    dddddddgdS )	Nr   paidz5Semantic + neural web search with content extraction.r   zExa API keyzhttps://exa.ai)keyr   rM   )r.   badgetagenv_varsr+   r,   s    r!   get_setup_schemaz%ExaWebSearchProvider.get_setup_schema   s4    J )++ 	
 
 	
r&   N)r   r   )r   r2   )r=   )r>   r   r?   r@   r   rA   )ra   rb   rc   r   r   rd   )r   rA   )__name__
__module____qualname____doc__propertyr.   r1   r6   r9   r<   rW   rv   r}   r+   r&   r!   r(   r(   U   s             X    X: : : :      (L (L (L (L (LT- - - -^
 
 
 
 
 
r&   r(   )r   r   )r   r#   )r   
__future__r   loggingr   typingr   r   r   agent.web_search_providerr   	getLoggerr~   rU   r"   r%   r(   r+   r&   r!   <module>r      s    0 # " " " " "  				 " " " " " " " " " " 7 7 7 7 7 7		8	$	$" " " "J   
 
 
 
 
, 
 
 
 
 
r&   