
    nSj                        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                 b   ddl m}  t          | dd          }||S ddlm}  |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get_provider_env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agent.web_search_providerr   
ValueErrortools.lazy_depsr   ImportError	Exceptionstrexa_pyr   headersr
   )_wtcachedr   r   _lazy_ensureexcr   clients           :/home/ice/.hermes/hermes-agent/plugins/web/exa/provider.py_get_exa_clientr#   )   s+    "!!!!!S-..F::::::}--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&   P   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_namec   r0   r'   boolc                >    ddl m} t           |d                    S )z=Return True when ``EXA_API_KEY`` is set to a non-empty value.r   r   r   )r   r   r3   )r.   r   s     r"   is_availablez!ExaWebSearchProvider.is_availableg   s.    >>>>>>$$]33444r'   c                    dS NTr,   r-   s    r"   supports_searchz$ExaWebSearchProvider.supports_searchm       tr'   c                    dS r7   r,   r-   s    r"   supports_extractz%ExaWebSearchProvider.supports_extractp   r9   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contents     )urltitledescriptionpositionweb)rE   dataNExa SDK not installed: zExa search error: %szExa search failed: )tools.interruptrC   loggerinfor#   search	enumerateresultsrG   appendrM   rN   joinr   r   r   r   warning)
r.   r=   r>   rC   responseweb_resultsiresultrG   r    s
             r"   rW   zExaWebSearchProvider.searchs   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   rB   c                    g | ]}|d dd	S )rD   rJ   )rM   rF   rN   r,   ).0us     r"   
<listcomp>z0ExaWebSearchProvider.extract.<locals>.<listcomp>   s/       HIACC  r'   zExa extract: %d URL(s)T)textrJ   )	sourceURLrN   )rM   rN   contentraw_contentmetadatac                8    g | ]}|d d t                    dS )rJ   rM   rN   rl   rF   )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 )rJ   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 )rJ   zExa extract failed: rp   r,   rq   s     r"   ri   z0ExaWebSearchProvider.extract.<locals>.<listcomp>   s?        B2@\WZ@\@\]]  r'   )rT   rC   rU   rV   lenr#   get_contentsrY   rj   rM   rN   rZ   r   r   r   r\   )r.   ra   rc   rC   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   r3   )r<   )r=   r   r>   r?   r   r@   )ra   rb   rc   r   r   rd   )r   r@   )__name__
__module____qualname____doc__propertyr/   r2   r5   r8   r;   rW   rv   r}   r,   r'   r"   r)   r)   W   s             X    X5 5 5 5      (L (L (L (L (LT- - - -^
 
 
 
 
 
r'   r)   )r   r   )r   r$   )r   
__future__r   loggingostypingr   r   r   r   r   	getLoggerr~   rU   r#   r&   r)   r,   r'   r"   <module>r      s    0 # " " " " "  				 " " " " " " " " " " 7 7 7 7 7 7		8	$	$$ $ $ $N   A
 A
 A
 A
 A
, A
 A
 A
 A
 A
r'   