+
    kHàjs,  ã                  ó.  € R t ^ RIHt ^ RIt^ RIHtHtHt ^ RIH	t	 ^ RI
t
^ RIHt ^ RIHt ]P                  ! ]4      tRtRtR	R
RRRR]! 4       P(                   2RR/t^tRtRtR R ltR R ltR R R lltR R ltR R ltR R lt ! R R]4      tR# )!uï  Perplexity web search + page snippets â€” plugin form.

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

- ``supports_search()``  -> True (Perplexity Search API ``POST /search``)
- ``supports_extract()`` -> True (``POST /sdk/content/snippets`` â€” the
  query-relevant page-excerpt route behind ``pplx content snippets``)

Both are sync â€” the underlying call is ``httpx.post(...)``.

Config keys this provider responds to::

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

Env vars::

    PERPLEXITY_API_KEY=...       # required for direct search and extract
    PERPLEXITY_BASE_URL=...      # optional override of https://api.perplexity.ai

No anonymous tier at Perplexity itself. The managed route serves search through
``perplexity-gateway.<TOOL_GATEWAY_DOMAIN>`` as ``search_type: "fast"``, which the
gateway serves to any Nous identity (paid, unpaid or anonymous guest); a direct key
takes precedence.
Managed extract stays on Firecrawl.

Extract caveat: Perplexity's only supported page-content route returns the
passages of a page relevant to a *query* (elisions marked ``â€¦``), not the
whole page. ``web_extract`` has no query, so the URL's own path words are
used as the relevance query, which approximates "what is this page about".
Use Firecrawl / Exa / Parallel when a verbatim full-page dump is required.
)ÚannotationsN)ÚAnyÚDictÚList)Úurlparse)ÚWebSearchProvider)Úget_version_infozhttps://api.perplexity.aiz%https://www.perplexity.ai/account/apizHTTP-Refererz%https://hermes-agent.nousresearch.comzX-TitlezHermes Agentú
User-AgentzHermesAgent/zX-Pplx-Integrationzhermes-agenti @  i   c               ó   € V ^8„  d   QhRR/# ©é   ÚreturnÚstr© )Úformats   "ÚD/home/ubuntu/.hermes/hermes-agent/plugins/web/perplexity/provider.pyÚ__annotate__r   G   s   € ÷ Eñ E˜Cñ Eó    c                 ó   € R \          2# )z,PERPLEXITY_API_KEY is not set. Get a key at ©Ú_KEY_URLr   r   r   Ú_missing_key_errorr   G   s   € Ø9¼(¸ÐDÐDr   c                ó\   € ^ RI Hp ^ RIHp V! 4       '       g   R# VP	                  V R7      # )zLNous Tool Gateway config when web_search is on the managed route, else None.)Úmanaged_tool_gateway©Ú_managed_web_searchN©Útoken_reader)Útoolsr   Útools.web_toolsr   Úresolve_free_search_gateway)r   Úgwr   s   &  r   Ú_managed_gatewayr"   K   s(   € å0Ý3á× Ò ÙØ×)Ñ)°|Ð)ÓDÐDr   c               ó$   € V ^8„  d   QhRRRRRR/# )r   Úendpointr   Úpayloadúdict[str, Any]r   r   )r   s   "r   r   r   U   s"   € ÷ "ñ " #ð "°ð "ÐQ_ñ "r   c           	     óŒ  € ^ RI Hp V! R4      p\        pVe7   VP                  P	                  R4      VP
                  R\        R,          /rTpMAV'       d'   V! R4      ;'       g    \        P	                  R4      pM\        \        4       4      hV RV P                  R4       2p\        P                  RW4       \        P                  ! VV^<RRV 2R	R
/VCR7      pVP                  R8¼  dG   VP                  ;'       g    RP!                  4       p	\        T	;'       g    RVP                   24      hVP#                  4       # )a  POST to Perplexity or the supplied gateway; return parsed JSON.

Raises ``ValueError`` when the key is missing or on any non-2xx status,
carrying the response body so Perplexity's own error text (invalid key,
BAD_REQUEST, rate limit) reaches the model verbatim.
©Úget_provider_envÚPERPLEXITY_API_KEYÚ/r	   ÚPERPLEXITY_BASE_URLzPerplexity %s request to %sÚAuthorizationzBearer zContent-Typezapplication/json)ÚjsonÚtimeoutÚheadersi�  Ú zHTTP )Úagent.web_search_providerr)   Ú_HEADERSÚgateway_originÚrstripÚnous_user_tokenÚ_DEFAULT_BASE_URLÚ
ValueErrorr   ÚlstripÚloggerÚinfoÚhttpxÚpostÚstatus_codeÚtextÚstripr.   )
r$   r%   Úgatewayr)   Úapi_keyr0   Úbase_urlÚurlÚresponseÚbodys
   &&&       r   Ú_perplexity_requestrG   U   sD  € õ ;áÐ3Ó4€GÜ€GØÒà%,×%;Ñ%;×%BÑ%BÀ3Ó%GÈ×I`ÑI`ÐcoÔqyð  {Gõ  rHð  cI˜7ˆ˜7ß	Ù$Ð%:Ó;×PÐPÔ?P×XÑXÐY\Ó]‰äÔ+Ó-Ó.Ð.ØˆJ�a˜Ÿ™¨Ó,Ð-Ð
.€CÜ
‡K�KÐ-¨xÔ=ä�zŠzØØØà˜w w iÐ0ØÐ.ð
ð ð
ô		€Hð ×Ñ˜sÔ"Ø—‘×#Ð# ×*Ñ*Ó,ˆÜ˜×?Ð? 5¨×)=Ñ)=Ð(>Ð!?Ó@Ð@Ø�=‰=‹?Ðr   c               ó    € V ^8„  d   QhRRRR/# )r   rE   r&   r   r   )r   s   "r   r   r   z   s   € ÷ ;ñ ;¨ð ;¸>ñ ;r   c                óL  € . p\        V P                  R4      ;'       g    . 4       Fs  w  r#TP                  RVP                  RR4      ;'       g    RRVP                  RR4      ;'       g    RRVP                  RR4      ;'       g    RRV^,           /4       Ku  	  RR	R
RV//# )zJMap Search API ``{results: [{title,url,snippet,...}]}`` to the tool shape.ÚresultsÚtitler1   rD   ÚdescriptionÚsnippetÚpositionÚsuccessTÚdataÚweb)Ú	enumerateÚgetÚappend)rE   Úweb_resultsÚiÚresults   &   r   Ú_normalize_search_resultsrX   z   s£   € à€KÜ˜xŸ|™|¨IÓ6×<Ð<¸"Ö=‰	ˆØ×Ñà˜Ÿ™ G¨RÓ0×6Ð6°BØ�v—z‘z %¨Ó,×2Ð2°Ø˜vŸz™z¨)°RÓ8×>Ð>¸BØ˜A �Eð	ö	
ñ >ð �t˜V e¨[Ð%9Ð:Ð:r   c               ó$   € V ^8„  d   QhRRRRRR/# )r   rE   r&   Úurlsú	list[str]r   úlist[dict[str, Any]]r   )r   s   "r   r   r   ‰   s"   € ÷ ñ  .ð ¸	ð ÐFZñ r   c                óÖ  € V P                  R4      ;'       g    .  Uu/ uF.  p\        V\        4      '       g   K  VP                  RR4      VbK0  	  pp. pV Fˆ  pVP                  V/ 4      pVP                  R4      ;'       g    RpRVRRRVRVRR	V//pVP                  R
4      p	V	'       g	   V'       g   V	'       d   \        V	4      MRVR
&   VP	                  V4       KŠ  	  V# u upi )u  Map ``{results: [{url,text?,tokens_count?,error?}]}`` to extract documents.

One document per requested URL, in request order. A URL the backend
omitted or flagged with ``error`` becomes a document carrying ``error``
rather than raising â€” a 200 does not mean every page succeeded.
rJ   rD   r1   r?   rK   ÚcontentÚraw_contentÚmetadataÚ	sourceURLÚerrorzno content returned)rS   Ú
isinstanceÚdictr   rT   )
rE   rZ   ÚrÚby_urlÚ	documentsrD   rW   r?   Údocrb   s
   &&        r   Ú_normalize_snippetsri   ‰   sé   € ð -5¯L©L¸Ó,C×,IÐ,IÀrÐ,IÓbÑ,I aÌjÐYZÔ\`×NaÔ!ˆa�e‰e�E˜2Ó Ò!Ñ,I€FÐbØ&(€IÛˆØ—‘˜C Ó$ˆØ�z‰z˜&Ó!×'Ð' Rˆà�3Ø�RØ�tØ˜4Ø˜ cÐ*ð
ˆð —
‘
˜7Ó#ˆßŸß).œ3˜uœ:Ð4IˆC�‰LØ×Ñ˜Öñ ð Ðùò! cs
   žC&»C&c               ó    € V ^8„  d   QhRRRR/# )r   rZ   r[   r   r   r   )r   s   "r   r   r   £   s   € ÷ 
9ñ 
9˜)ð 
9¨ñ 
9r   c                óX  € . pV  Fë  p\        V4      pVP                  P                  RR4      P                  RR4      P                  RR4      P                  4        FQ  pVP	                  4       V9  g   K  VP                  4       '       d   K2  VP                  VP	                  4       4       KS  	  VP                  P                  R4      '       d   KÐ  VP                  VP                  4       Kí  	  RP                  V4      R,          ;'       g    RP                  V 4      R,          # )zUDerive a relevance query from URL path words (``/bloom-filter`` -> ``bloom filter``).Ú-Ú Ú_r+   :Niô  N)
r   ÚpathÚreplaceÚsplitÚlowerÚisdigitrT   r@   ÚnetlocÚjoin)rZ   ÚwordsrD   ÚparsedÚtokens   &    r   Ú_query_for_urlsry   £   sÕ   € à€EÛˆÜ˜#“ˆØ—[‘[×(Ñ(¨¨cÓ2×:Ñ:¸3ÀÓD×LÑLÈSÐRUÓV×\Ñ\Ö^ˆEØ�{‰{‹} EÖ)°%·-±-·/´/Ø—‘˜UŸ[™[›]Ö+ñ _ð �{‰{× Ñ  ×%Ô%Ø�L‰L˜Ÿ™Ö'ñ ð �8‰8�E‹?˜4Õ ×8Ð8 C§H¡H¨T£N°4Õ$8Ð8r   c                  ó’   € ] tR t^°tRt]R R l4       t]R R l4       tR R ltR R	 lt	R
 R lt
RR R lltR R ltR R ltRtR# )ÚPerplexityWebSearchProviderzBDirect or managed search; direct-key content snippets for extract.c               ó   € V ^8„  d   QhRR/# r   r   )r   s   "r   r   Ú(PerplexityWebSearchProvider.__annotate__´   s   € ÷ ñ �cñ r   c                	ó   € R # )Ú
perplexityr   ©Úselfs   &r   ÚnameÚ PerplexityWebSearchProvider.name³   ó   € ár   c               ó   € V ^8„  d   QhRR/# r   r   )r   s   "r   r   r}   ¸   s   € ÷ ñ ˜cñ r   c                	ó   € R # )Ú
Perplexityr   r€   s   &r   Údisplay_nameÚ(PerplexityWebSearchProvider.display_name·   r„   r   c               ó   € V ^8„  d   QhRR/# ©r   r   Úboolr   )r   s   "r   r   r}   »   s   € ÷ Añ A˜dñ Ar   c                óh   € ^ RI Hp ^ RIHp \	        V! R4      4      ;'       g    \        VR7      RJ# )z\True with a ``PERPLEXITY_API_KEY``, or on the managed route with a likely-usable Nous token.r(   )Úpeek_nous_access_tokenr*   r   N)r2   r)   Útools.managed_tool_gatewayrŽ   rŒ   r"   )r�   r)   rŽ   s   &  r   Úis_availableÚ(PerplexityWebSearchProvider.is_available»   s@   € å>ÝEäÑ$Ð%9Ó:Ó;÷  Að  AÔ?OÐ]sÔ?tð  }Að  @Að  	Ar   c               ó   € V ^8„  d   QhRR/# r‹   r   )r   s   "r   r   r}   Â   s   € ÷ ñ  ñ r   c                	ó   € R # ©Tr   r€   s   &r   Úsupports_searchÚ+PerplexityWebSearchProvider.supports_searchÂ   ó   € Ùr   c               ó   € V ^8„  d   QhRR/# r‹   r   )r   s   "r   r   r}   Å   s   € ÷ ñ  $ñ r   c                	ó   € R # r”   r   r€   s   &r   Úsupports_extractÚ,PerplexityWebSearchProvider.supports_extractÅ   r—   r   c               ó$   € V ^8„  d   QhRRRRRR/# )r   Úqueryr   ÚlimitÚintr   r&   r   )r   s   "r   r   r}   È   s&   € ÷ 'Sñ 'S˜Cð 'S¨ð 'S°Nñ 'Sr   c           
     ó‚  €  ^ RI Hp V! 4       '       d   RRRR/# ^ RIHp ^ RIHp ^ RIHp \        V! R	4      4      pV'       d   RMV! 4       pV'       d   V! 4       MR
p	V	f$   V'       d   ^ RI	H
p
Hp \        V! RV
R4      4      h\        P                  RYV	'       d   RMR4       RVR\        ^\!        V\"        4      4      RR/pV	e   RVR&   \%        RWÉ4      p\'        V4      #   \         d   pRRR\)        T4      /u R
p?# R
p?i\*         d*   p\        P-                  RT4       RRRRT 2/u R
p?# R
p?ii ; i)zêExecute a Perplexity Search API query.

``search_context_size: low`` keeps ``snippet`` at description length;
the default (``high``) returns multi-KB page excerpts per hit, which
belongs in ``web_extract`` rather than a results list.
©Úis_interruptedrO   Frb   ÚInterruptedr(   )r    r   r*   N)ÚNOUS_MANAGED_PROVIDERÚselection_errorrQ   z8there is no usable Nous identity (sign in with `/login`)z$Perplexity search: '%s' (limit=%d%s)z	, managedr1   r�   Úmax_resultsÚsearch_context_sizeÚlowÚfastÚsearch_typeÚsearchzPerplexity search error: %szPerplexity search failed: )Útools.interruptr¢   r2   r)   r�   r    r   r   rŒ   Útools.tool_backend_helpersr¤   r¥   r8   r:   r;   ÚmaxÚminÚ_MAX_SEARCH_RESULTSrG   rX   r   Ú	ExceptionÚwarning)r�   r�   rž   r¢   r)   r    r   ÚdirectÚmanagedrA   r¤   r¥   r%   ÚrawÚexcs   &&&            r   r«   Ú"PerplexityWebSearchProvider.searchÈ   s:  € ð 	SÝ6á×ÒØ! 5¨'°=ÐAÐAåBÝNÝ;äÑ*Ð+?Ó@ÓAˆFß%‘eÑ+>Ó+@ˆGß7>Ñ1Ô3ÀDˆGØŠ§7ß]ä ¡ØÐ0Ð2ló"nó oð oä�K‰KÐ>À×]dÉkÐjlÔmà˜Øœs 1¤c¨%Ô1DÓ&EÓFØ% uðˆGð
 Ò"Ø)/�˜Ñ&Ü% h°ÓAˆCÜ,¨SÓ1Ð1øÜô 	9Ø˜u g¬s°3«xÐ8Õ8ûÜô 	SÜ�N‰NÐ8¸#Ô>Ø˜u gÐ1KÈCÈ5Ð/QÐRÕRûð	SúsL   ‚C( •C( ›;C( ÁC( Á,A;C( Ã(D>Ã3DÄD>ÄD>ÄD>ÄD9Ä3D>Ä9D>c               ó$   € V ^8„  d   QhRRRRRR/# )r   rZ   r[   Úkwargsr   r   r\   r   )r   s   "r   r   r}   ñ   s"   € ÷ ñ ˜Ið °ð Ð9Mñ r   c                óB  €  ^ RI Hp V! 4       '       d   V Uu. uF  pRVRRRR/NK  	  up# \        P                  R\	        V4      4       \        RR	\        V4      R
\        V4      R\        R\        /4      p\        V\        V4      4      # u upi   \         d4   pT Uu. uF  pRTRRRRR\        T4      /NK  	  Mu upi upu Rp?# Rp?i\         dD   p\        P                  RT4       T Uu. uF  pRTRRRRRRT 2/NK  	  Mu upi upu Rp?# Rp?ii ; i)uµ   Return query-relevant snippets for one or more URLs.

Sync â€” the underlying call is httpx.post(...). Per-URL failures
become items with ``error``; a missing key errors every URL.
r¡   rD   rb   r£   rK   r1   zPerplexity snippets: %d URL(s)zsdk/content/snippetsr�   rZ   Ú
max_tokensÚmax_tokens_per_pager^   NzPerplexity extract error: %szPerplexity extract failed: )r¬   r¢   r:   r;   ÚlenrG   ry   ÚlistÚ_MAX_TOKENSÚ_MAX_TOKENS_PER_PAGEri   r8   r   r±   r²   )r�   rZ   r¹   r¢   Úurµ   r¶   s   &&,    r   ÚextractÚ#PerplexityWebSearchProvider.extractñ   s(  € ð	Ý6á×ÒÙQUÓVÑQUÈA˜  7¨M¸7ÀBÓGÑQUÑVÐVä�K‰KÐ8¼#¸d»)ÔDÜ%Ø&àœ_¨TÓ2ØœD ›JØ ¤+Ø)Ô+?ð	óˆCô ' s¬D°«JÓ7Ð7ùò Wøô ô 	]ÙW[Ó\ÑW[ÐRS�U˜A˜w¨¨I°r¸7ÄCÈÃHÓMÒW[ùÔ\Õ\ûÜô 	Ü�N‰NÐ9¸3Ô?ñ óá�Að ˜˜7 B¨	°2°wÐB]Ð^aÐ]bÐ@cÓdÚùôõ ûð	úsh   ‚B •B ™BªB ­A!B ÂB ÂDÂCÂ#C Â?CÃDÃDÃDÃDÃ5DÄDÄDÄDc               ó   € V ^8„  d   QhRR/# )r   r   r&   r   )r   s   "r   r   r}     s   € ÷ 
ñ 
 .ñ 
r   c                	ó0   € R RRRRRRRRR	R
R\         /.RR/# )r‚   r‡   ÚbadgeÚpaidÚtagui   Perplexity Search API â€” ranked, date-stamped web results plus query-relevant page snippets for extract.Úenv_varsÚkeyr*   ÚpromptzPerplexity API keyrD   Úweb_tierr   r€   s   &r   Úget_setup_schemaÚ,PerplexityWebSearchProvider.get_setup_schema  sE   € à�LØ�VØð<ð àÐ/ØÐ2Øœ8ððð ˜ð
ð 	
r   r   N)é   )Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__Úpropertyr‚   rˆ   r�   r•   rš   r«   rÂ   rÍ   Ú__static_attributes__r   r   r   r{   r{   °   sR   † ÙLàôó ðð ôó ðõAõõ÷'SõR÷@
ñ 
r   r{   )N) rÔ   Ú
__future__r   ÚloggingÚtypingr   r   r   Úurllib.parser   r<   r2   r   Úhermes_cli.version_infor   Ú	getLoggerrÐ   r:   r7   r   Úbase_versionr3   r°   r¿   rÀ   r   r"   rG   rX   ri   ry   r{   r   r   r   Ú<module>rÞ      s³   ðñ"õH #ã ß "Ñ "Ý !ã å 7Ý 4à	×	Ò	˜8Ó	$€à/Ð Ø2€ð Ð;Øˆ~Ø�LÑ!1Ó!3×!@Ñ!@Ð AÐBØ˜.ð	€ð Ð à€ØÐ õEôE÷"õJ;õõ4
9ôq
Ð"3ö q
r   