
    PL
ji'                        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     A/home/kuhnn/.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 t          j        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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osgetenv
ValueErrorr   parallelr   r   )_wtcachedr   r   clients        r   _get_sync_clientr&   @   s     "!!!!!S,d33Fi*++G 
6
 
 	

 #$$$!!!!!!Xg&&&F!CM    c                     ddl m}  t          | dd          }||S t          j        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   )AsyncParallelr   )
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i*++G 
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-   u   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fastr0   )r   r    lowerstrip)modes    r   _resolve_search_moder6      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   r>   r'   boolc                j    t          t          j        dd                                                    S )zBReturn True when ``PARALLEL_API_KEY`` is set to a non-empty value.r    )rA   r   r    r4   r;   s    r   is_availablez&ParallelWebSearchProvider.is_available   s)    BI0"55;;==>>>r'   c                    dS NTr:   r;   s    r   supports_searchz)ParallelWebSearchProvider.supports_search       tr'   c                    dS rF   r:   r;   s    r   supports_extractz*ParallelWebSearchProvider.supports_extract   rH   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	objectiver5   max_resultsrC       )urltitledescriptionpositionTweb)rT   dataNParallel SDK not installed: zParallel search error: %szParallel search failed: )tools.interruptrR   r6   loggerinfor&   betasearchmin	enumerateresultsexcerptsappendr\   r]   joinr!   r   r   r   warning)r<   rL   rM   rR   r5   responseweb_resultsiresultrk   r   s              r   rg   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   rQ   c                    g | ]}|d dd	S )rS   rC   )r\   rU   r]   r:   ).0us     r   
<listcomp>z5ParallelWebSearchProvider.extract.<locals>.<listcomp>   s/       HIACC  r'   zParallel extract: %d URL(s)T)rs   full_contentNrC   z

)	sourceURLr]   )r\   r]   contentraw_contentmetadatazextraction failedr}   )r\   r]   r~   rU   r   c                8    g | ]}|d d t                    dS )rC   r\   r]   r~   rU   )r   ry   rz   r   s     r   r{   z5ParallelWebSearchProvider.extract.<locals>.<listcomp>
  s,    \\\RSArCHHMM\\\r'   c                $    g | ]}|d d d dS )rC   rb   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 )rC   zParallel extract failed: r   r:   r   s     r   r{   z5ParallelWebSearchProvider.extract.<locals>.<listcomp>  s?        B2@a\_@a@abb  r'   )rc   rR   rd   re   lenr+   rf   extractrj   r|   rm   rk   r\   r]   rl   errorsr~   
error_typer!   r   r   rn   )r<   rs   ru   rR   ro   rj   rr   r~   r\   r]   rU   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   rA   )rK   )rL   r   rM   rN   r   rO   )rs   rt   ru   r   r   rv   )r   rO   )__name__
__module____qualname____doc__propertyr=   r@   rD   rG   rJ   rg   r   r   r:   r'   r   r8   r8      s        66   X    X? ? ? ?      .Q .Q .Q .Q .Q`A A A AF
 
 
 
 
 
r'   r8   )r   r	   )r   r   r   )r   
__future__r   loggingr   typingr   r   r   agent.web_search_providerr   	getLoggerr   rd   r   r&   r+   r-   _get_parallel_client_get_async_parallel_clientr6   r8   r:   r'   r   <module>r      s(   8 # " " " " "  				 " " " " " " " " " " 7 7 7 7 7 7		8	$	$$ $ $ $$   6   4	& 	& 	& 	& ( .    T
 T
 T
 T
 T
 1 T
 T
 T
 T
 T
r'   