
    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ddZddZeZeZddZ G d de
          ZdS )u  Parallel.ai web search + content extraction — plugin form.

Subclasses :class:`agent.web_search_provider.WebSearchProvider`. Uses two
distinct Parallel SDK clients:

- ``Parallel`` (sync)        — for :meth:`search`
- ``AsyncParallel`` (async)  — for :meth:`extract`

This is the first plugin to exercise the **async-extract** code path in
the ABC: :meth:`extract` is declared ``async def``, and the dispatcher
in :func:`tools.web_tools.web_extract_tool` detects coroutines via
:func:`inspect.iscoroutinefunction` and awaits.

Config keys this provider responds to::

    web:
      search_backend: "parallel"      # explicit per-capability
      extract_backend: "parallel"     # explicit per-capability
      backend: "parallel"             # shared fallback
      # Optional: search mode (default "agentic"; also "fast" or "one-shot")
      # via the PARALLEL_SEARCH_MODE env var.

Env vars::

    PARALLEL_API_KEY=...             # https://parallel.ai (required)
    PARALLEL_SEARCH_MODE=agentic     # optional: agentic|fast|one-shot
    )annotationsN)AnyDictList)WebSearchProviderreturnNonec                     	 ddl m}   | dd           dS # t          $ r Y dS t          $ r!}t          t	          |                    d}~ww xY w)aM  Trigger lazy install of the parallel SDK if it isn't present.

    Mirrors the lazy-deps pattern used by the legacy implementation.
    Swallows benign ImportError from the lazy_deps helper itself; if the
    SDK is genuinely missing the subsequent ``from parallel import ...``
    raises ImportError that the caller can handle.
    r   )ensurezsearch.parallelF)promptN)tools.lazy_depsr   ImportError	Exceptionstr)_lazy_ensureexcs     ?/home/ice/.hermes/hermes-agent/plugins/web/parallel/provider.py_ensure_parallel_sdk_installedr   .   s    $::::::&u555555    $ $ $#c((###$s    
A	AA		Ar   c                     ddl m}  t          | dd          }||S ddlm}  |d          }|st          d          t                       ddlm}  ||          }|| _	        |S )	zLazy-load + cache the sync Parallel client.

    Cache lives on :mod:`tools.web_tools` (as ``_parallel_client``) so unit
    tests that reset that name between cases keep working.
    r   N_parallel_clientget_provider_envPARALLEL_API_KEYVPARALLEL_API_KEY environment variable not set. Get your API key at https://parallel.ai)Parallelapi_key)
tools.web_tools	web_toolsgetattragent.web_search_providerr   
ValueErrorr   parallelr   r   )_wtcachedr   r   r   clients         r   _get_sync_clientr'   @   s     "!!!!!S,d33F::::::122G 
6
 
 	

 #$$$!!!!!!Xg&&&F!CM    c                     ddl m}  t          | dd          }||S ddlm}  |d          }|st          d          t                       ddlm}  ||          }|| _	        |S )	z}Lazy-load + cache the async Parallel client.

    Cache lives on :mod:`tools.web_tools` (as ``_async_parallel_client``).
    r   N_async_parallel_clientr   r   r   )AsyncParallelr   )
r   r   r    r!   r   r"   r   r#   r+   r*   )r$   r%   r   r   r+   r&   s         r   _get_async_clientr,   ]   s    
 "!!!!!S2D99F::::::122G 
6
 
 	

 #$$$&&&&&&]7+++F!'CMr(   c                 .    ddl m}  d| _        d| _        dS )zDrop both cached clients so tests can re-instantiate cleanly.

    Clears the canonical slots on :mod:`tools.web_tools` (where
    :func:`_get_sync_client` / :func:`_get_async_client` read/write them).
    r   N)r   r   r   r*   )r$   s    r   _reset_clients_for_testsr.   y   s,     "!!!!!C!%Cr(   r   c                     t          j        dd                                                                          } | dvrd} | S )zDReturn the validated PARALLEL_SEARCH_MODE value (default "agentic").PARALLEL_SEARCH_MODEagentic>   one-shotfastr1   )osgetenvlowerstrip)modes    r   _resolve_search_moder9      sB    9+Y77==??EEGGD222Kr(   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 )ParallelWebSearchProviderz,Parallel.ai search + async extract provider.r   r   c                    dS )Nr#    selfs    r   namezParallelWebSearchProvider.name       zr(   c                    dS )Nr   r=   r>   s    r   display_namez&ParallelWebSearchProvider.display_name   rA   r(   boolc                >    ddl m} t           |d                    S )zBReturn True when ``PARALLEL_API_KEY`` is set to a non-empty value.r   r   r   )r!   r   rD   )r?   r   s     r   is_availablez&ParallelWebSearchProvider.is_available   s/    >>>>>>$$%788999r(   c                    dS NTr=   r>   s    r   supports_searchz)ParallelWebSearchProvider.supports_search       tr(   c                    dS rH   r=   r>   s    r   supports_extractz*ParallelWebSearchProvider.supports_extract   rJ   r(      querylimitintDict[str, Any]c           	        	 ddl m}  |            rdddS t                      }t                              d|||           t                      j                            |g||t          |d                    }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 a Parallel search (sync).

        Uses the ``beta.search`` endpoint with the configured mode
        (``PARALLEL_SEARCH_MODE`` env var, default "agentic"). Limit is
        capped at 20 server-side.
        r   is_interruptedFInterrupted)successerrorz)Parallel search: '%s' (mode=%s, limit=%d)   )search_queries	objectiver8   max_results     )urltitledescriptionpositionTweb)rV   dataNParallel SDK not installed: zParallel search error: %szParallel search failed: )tools.interruptrT   r9   loggerinfor'   betasearchmin	enumerateresultsexcerptsappendr_   r`   joinr"   r   r   r   warning)r?   rN   rO   rT   r8   responseweb_resultsiresultrn   r   s              r   rj   z ParallelWebSearchProvider.search   s)   '	Q666666~ B#(=AAA'))DKK;UD%   ()).55 %wrNN	 6  H K&x'7'=2>> 	 		6!?0b""%z/R!'!3=E'Msxx'9'9'92$%E	      $e[-ABBB 	9 	9 	9$s3xx88888888 	 	 	 ===         	Q 	Q 	QNN6<<<$/O#/O/OPPPPPPPP	QsG   C+ CC+ +
E 5DE E D& E &E 3"EE E urls	List[str]kwargsr   List[Dict[str, Any]]c           
     F  K   	 ddl m}  |            rd |D             S t                              dt	          |                     t                      j                            |d           d{V }g }|j        pg D ]Y}|j	        pd}|sd	
                    |j        pg           }|j        pd}|j        pd}	|                    ||	||||	d
d           Z|j        pg D ];}
|                    |
j        pddd|
j        p|
j        pdd|
j        pdi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)uS  Extract content from one or more URLs via the async SDK.

        Returns the legacy list-of-results shape that
        :func:`tools.web_tools.web_extract_tool` expects: one entry per
        successful URL plus one entry per failed URL with an ``error``
        field. Errors are not raised — they're returned as per-URL items.
        r   rS   c                    g | ]}|d dd	S )rU   r\   )r_   rW   r`   r=   ).0us     r   
<listcomp>z5ParallelWebSearchProvider.extract.<locals>.<listcomp>   s/       HIACC  r(   zParallel extract: %d URL(s)T)rv   full_contentNr\   z

)	sourceURLr`   )r_   r`   contentraw_contentmetadatazextraction failedr   )r_   r`   r   rW   r   c                8    g | ]}|d d t                    dS )r\   r_   r`   r   rW   )r   r|   r}   r   s     r   r~   z5ParallelWebSearchProvider.extract.<locals>.<listcomp>  s,    \\\RSArCHHMM\\\r(   c                $    g | ]}|d d d dS )r\   re   r   r=   r   s     r   r~   z5ParallelWebSearchProvider.extract.<locals>.<listcomp>  s?        B2@d_b@d@dee  r(   zParallel extract error: %sc                $    g | ]}|d d d dS )r\   zParallel extract failed: r   r=   r   s     r   r~   z5ParallelWebSearchProvider.extract.<locals>.<listcomp>  s?        B2@a\_@a@abb  r(   )rf   rT   rg   rh   lenr,   ri   extractrm   r   rp   rn   r_   r`   ro   errorsr   
error_typer"   r   r   rq   )r?   rv   rx   rT   rr   rm   ru   r   r_   r`   rW   r   s              @r   r   z!ParallelWebSearchProvider.extract   s     7	666666~  MQ    KK5s4yyAAA.005==! >        H
 -/G"*0b   -3 A$kk&/*?R@@Gj&B*"!&#*'.25$F$F     ".B 	 	$yB!##%!&!Y%2B!YFY%0%)/r$B     N 	] 	] 	]\\\\W[\\\\\\\\\ 	 	 	             	 	 	NN7===           	sG   D# DD# #
F -E :F  F E F  F -(FF F c                    dddddddgdS )	Nr   paidz2Objective-tuned search + parallel page extraction.r   zParallel API keyzhttps://parallel.ai)keyr   r_   )r@   badgetagenv_varsr=   r>   s    r   get_setup_schemaz*ParallelWebSearchProvider.get_setup_schema  s4    G .00 	
 
 	
r(   Nr   r   )r   rD   )rM   )rN   r   rO   rP   r   rQ   )rv   rw   rx   r   r   ry   )r   rQ   )__name__
__module____qualname____doc__propertyr@   rC   rF   rI   rL   rj   r   r   r=   r(   r   r;   r;      s        66   X    X: : : :      .Q .Q .Q .Q .Q`A A A AF
 
 
 
 
 
r(   r;   )r   r	   )r   r   r   )r   
__future__r   loggingr4   typingr   r   r   r!   r   	getLoggerr   rg   r   r'   r,   r.   _get_parallel_client_get_async_parallel_clientr9   r;   r=   r(   r   <module>r      s(   8 # " " " " "  				 " " " " " " " " " " 7 7 7 7 7 7		8	$	$$ $ $ $$   :   8	& 	& 	& 	& ( .    V
 V
 V
 V
 V
 1 V
 V
 V
 V
 V
r(   