
    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	 dddZ G d de
          ZdS )u  Tavily web search + content extraction + crawl — plugin form.

Subclasses :class:`agent.web_search_provider.WebSearchProvider`. Three
capabilities advertised:

- ``supports_search()``  -> True (Tavily ``/search``)
- ``supports_extract()`` -> True (Tavily ``/extract``)
- ``supports_crawl()``   -> True (Tavily ``/crawl``) — sync HTTP crawl;
  Firecrawl also advertises ``supports_crawl=True`` (async)

All three are sync — the underlying call is ``httpx.post(...)``. The
dispatcher in :func:`tools.web_tools.web_crawl_tool` (which is itself
async) will run sync providers in a thread when appropriate.

Config keys this provider responds to::

    web:
      search_backend: "tavily"     # explicit per-capability
      extract_backend: "tavily"    # explicit per-capability
      crawl_backend: "tavily"      # explicit per-capability
      backend: "tavily"            # shared fallback for all three

Env vars::

    TAVILY_API_KEY=...           # https://app.tavily.com/home (required)
    TAVILY_BASE_URL=...          # optional override of https://api.tavily.com

Auth note: Tavily uses ``api_key`` in the JSON body for /search and
/extract, but **also requires** ``Authorization: Bearer <key>`` for /crawl
(body-only auth returns 401 on /crawl). The plugin handles both.
    )annotationsN)AnyDictList)WebSearchProviderendpointstrpayloadDict[str, Any]returnc                   ddl }t          j        d          }|st          d          t          j        dd          }t	          |          }||d<   | d|                     d           }t                              d	| |           |                     d          d
k    rdd| ini }|	                    |||d          }|
                                 |                                S )zPOST to the Tavily API and return the parsed JSON response.

    Mirrors :func:`tools.web_tools._tavily_request`. Raises ``ValueError``
    when ``TAVILY_API_KEY`` is unset; the caller catches and surfaces as
    a typed error response.
    r   NTAVILY_API_KEYz\TAVILY_API_KEY environment variable not set. Get your API key at https://app.tavily.com/homeTAVILY_BASE_URLzhttps://api.tavily.comapi_key/zTavily %s request to %scrawlAuthorizationzBearer <   )jsonheaderstimeout)httpxosgetenv
ValueErrordictlstriploggerinfostrippostraise_for_statusr   )r   r
   r   r   base_urlurlr   responses           ?/home/kuhnn/.hermes/hermes-agent/plugins/web/tavily/provider.py_tavily_requestr'   ,   s    LLLi())G 
>
 
 	

 y*,DEEH7mmG GI
.
.,,
.
.C
KK)8S999 9As8K8Kw8V8V 3' 3 344\^Gzz#GWbzIIH==??    r%   c           	        g }t          |                     dg                     D ]^\  }}|                    |                    dd          |                    dd          |                    dd          |dz   d           _dd	|id
S )zEMap Tavily ``/search`` response to ``{success, data: {web: [...]}}``.resultstitle r$   content   )r+   r$   descriptionpositionTweb)successdata)	enumerategetappend)r%   web_resultsiresults       r&    _normalize_tavily_search_resultsr:   K   s    Kx||Ir::;; 
 
	6GR00zz%,,%zz)R88E	 	
 	
 	
 	
 e[%9:::r(   r,   fallback_urlList[Dict[str, Any]]c                   g }|                      dg           D ]}|                     d|          }|                     dd          p|                     dd          }|                    ||                     dd          ||||                     dd          dd           |                      d	g           D ]\}|                    |                     d|          ddd|                     d
d          d|                     d|          id           ]|                      dg           D ]F}t          |t                    r|nt          |          }|                    |ddddd|id           G|S )aB  Map Tavily ``/extract`` or ``/crawl`` response to standard documents.

    Documents follow the legacy LLM post-processing shape::

        {"url", "title", "content", "raw_content", "metadata"}

    Failures (``failed_results``, ``failed_urls``) become result entries
    with an ``error`` field rather than raising.
    r*   r$   raw_contentr,   r-   r+   )	sourceURLr+   )r$   r+   r-   r>   metadatafailed_resultserrorzextraction failedr?   )r$   r+   r-   r>   rB   r@   failed_urls)r5   r6   
isinstancer	   )	r%   r;   	documentsr9   r$   rawfailfail_urlurl_strs	            r&   _normalize_tavily_documentsrJ   Z   s    ')I,,y"-- 
 
jj--jj++Hvzz)R/H/HGR00"*-

7B8O8OPP 	
 	
 	
 	
 -r22 

 

xx|44!'+>??($((5,*G*GH 		
 		
 		
 		
 LL33 
 
(377J((S]]!,('2 		
 		
 		
 		
 r(   c                      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
Z
dddZddZd dZd!dZdS )"TavilyWebSearchProviderz)Tavily search + extract + crawl provider.r   r	   c                    dS )Ntavily selfs    r&   namezTavilyWebSearchProvider.name       xr(   c                    dS )NTavilyrO   rP   s    r&   display_namez$TavilyWebSearchProvider.display_name   rS   r(   boolc                j    t          t          j        dd                                                    S )z@Return True when ``TAVILY_API_KEY`` is set to a non-empty value.r   r,   )rW   r   r   r    rP   s    r&   is_availablez$TavilyWebSearchProvider.is_available   s)    BI.3399;;<<<r(   c                    dS NTrO   rP   s    r&   supports_searchz'TavilyWebSearchProvider.supports_search       tr(   c                    dS r[   rO   rP   s    r&   supports_extractz(TavilyWebSearchProvider.supports_extract   r]   r(   c                    dS r[   rO   rP   s    r&   supports_crawlz&TavilyWebSearchProvider.supports_crawl   r]   r(      querylimitintr   c                   	 ddl m}  |            rdddS t                              d||           t	          d|t          |d          ddd	          }t          |          S # t          $ r}dt          |          dcY d
}~S d
}~wt          $ r-}t          
                    d|           dd| dcY d
}~S d
}~ww xY w)zExecute a Tavily search.r   is_interruptedFInterrupted)r2   rB   zTavily search: '%s' (limit=%d)search   )rc   max_resultsinclude_raw_contentinclude_imagesNzTavily search error: %szTavily search failed: )tools.interruptrh   r   r   r'   minr:   r   r	   	Exceptionwarning)rQ   rc   rd   rh   rF   excs         r&   rj   zTavilyWebSearchProvider.search   s'   	O666666~ B#(=AAAKK8%GGG!"#&ub>>+0&+	  C 4C888 	9 	9 	9$s3xx88888888 	O 	O 	ONN4c:::$/M/M/MNNNNNNNN	Os5   A% AA% %
C /B C C "B;5C ;C urls	List[str]kwargsr   r<   c                   	 ddl m}  |            rd |D             S t                              dt	          |                     t          d|dd          }t          ||r|d         nd	          S # 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)u   Extract content from one or more URLs via Tavily.

        Sync — the underlying call is httpx.post(...). Returns the legacy
        list-of-results shape; per-URL failures become items with ``error``.
        r   rg   c                    g | ]}|d dd	S )ri   r,   )r$   rB   r+   rO   ).0us     r&   
<listcomp>z3TavilyWebSearchProvider.extract.<locals>.<listcomp>   s/       HIACC  r(   zTavily extract: %d URL(s)extractF)rt   rn   r,   r;   c                8    g | ]}|d d t                    dS )r,   r$   r+   r-   rB   )r	   ry   rz   rs   s     r&   r{   z3TavilyWebSearchProvider.extract.<locals>.<listcomp>   s,    \\\RSArCHHMM\\\r(   NzTavily extract error: %sc                $    g | ]}|d d d dS )r,   zTavily extract failed: r   rO   r   s     r&   r{   z3TavilyWebSearchProvider.extract.<locals>.<listcomp>   s?        B2@_Z]@_@_``  r(   )
ro   rh   r   r   lenr'   rJ   r   rq   rr   )rQ   rt   rv   rh   rF   rs   s        @r&   r|   zTavilyWebSearchProvider.extract   sr   	666666~  MQ    KK3SYY???! &+  C /T"9$q''r     	] 	] 	]\\\\W[\\\\\\\\\ 	 	 	NN5s;;;           	s5   A5 AA5 5
C?BCC(CCCr$   c                $   	 ddl m}  |            r
d|ddddgiS |                    d          }|                    dd	          }|                    d
d          }t                              d|||           |||d}|r||d<   t          d|          }dt          ||          iS # t          $ r!}	d|ddt          |	          dgicY d}	~	S d}	~	wt          $ r2}	t          
                    d|	           d|ddd|	 dgicY d}	~	S d}	~	ww xY w)u  Crawl a seed URL via Tavily's ``/crawl`` endpoint.

        Accepted kwargs (others ignored for forward compat):
          - ``instructions``: str — natural-language guidance for the crawl
          - ``depth``: str — ``"basic"`` (default) or ``"advanced"``
          - ``limit``: int — max pages to crawl (default 20)

        Returns ``{"results": [...]}`` shaped to match what
        :func:`tools.web_tools.web_crawl_tool` post-processes.
        r   rg   r*   r,   ri   r   instructionsdepthbasicrd   rk   z%Tavily crawl: %s (depth=%s, limit=%d))r$   rd   extract_depthr   r}   NzTavily crawl error: %szTavily crawl failed: )ro   rh   r5   r   r   r'   rJ   r   r	   rq   rr   )
rQ   r$   rv   rh   r   r   rd   r
   rF   rs   s
             r&   r   zTavilyWebSearchProvider.crawl   s   $	666666~ g!C"Vc$d$d#eff!::n55LJJw00EJJw++EKK?eUSSS!&' 'G
  7*6'!'733C6sMMM   	^ 	^ 	^bRRUVYRZRZ [ [\]]]]]]] 	 	 	NN3S999"!##%!>!>!>	 	 	 	 	 	 	 		s5   B* BB* *
D4C
DD'D
D
Dc                    dddddddgdS )	NrU   paidz)Search + extract + crawl in one provider.r   zTavily API keyzhttps://app.tavily.com/home)keypromptr$   )rR   badgetagenv_varsrO   rP   s    r&   get_setup_schemaz(TavilyWebSearchProvider.get_setup_schema  s4    > ,.8 	
 
 	
r(   N)r   r	   )r   rW   )rb   )rc   r	   rd   re   r   r   )rt   ru   rv   r   r   r<   )r$   r	   rv   r   r   r   )r   r   )__name__
__module____qualname____doc__propertyrR   rV   rY   r\   r_   ra   rj   r|   r   r   rO   r(   r&   rL   rL      s       33   X    X= = = =         O O O O O2       D/ / / /b
 
 
 
 
 
r(   rL   )r   r	   r
   r   r   r   )r%   r   r   r   )r,   )r%   r   r;   r	   r   r<   )r   
__future__r   loggingr   typingr   r   r   agent.web_search_providerr   	getLoggerr   r   r'   r:   rJ   rL   rO   r(   r&   <module>r      s    @ # " " " " "  				 " " " " " " " " " " 7 7 7 7 7 7		8	$	$   >; ; ; ;  350 0 0 0 0fP
 P
 P
 P
 P
/ P
 P
 P
 P
 P
r(   