Ë
    âß²jL-  ã                  ó>  — U d Z ddlmZ ddlmZ ddlZddlZddlZddl	Z	ddl
Z
ddlZddlmZmZmZ ddlmZmZmZmZmZ  ej,                  e«      ZdZdZdZdZd	ed
<   daded<    G d„ de«      Z dd„Z!dd„Z"edœdd„Z#dd„Z$dd„Z%dd„Z& G d„ de«      Z'ddiZ(d„ Z)y) uØ  DuckDuckGo search via the optional ``ddgs`` package (search only, no key). ``is_available()``
reflects package importability; the plugin registers either way so ``hermes tools`` can offer to
install it. Isolation: ``ddgs``/``primp`` can block inside native code while holding the GIL, so a
thread-pool ``future.result(timeout=â€¦)`` cap can never fire and Ctrl+C/SIGTERM freeze the process â€”
each search runs in a disposable child process the parent can terminate/kill.
é    )ÚannotationsN)ÚAnyÚDictÚOptional)ÚBaseWebSearchProviderÚsearch_failÚ	search_okÚsetup_schemaÚ	title_hité   gš™™™™™¹?g      ð?zOptional[str]Ú
_test_hookúOptional[subprocess.Popen]Ú_last_worker_procc                  ó   — e Zd ZdZy)Ú_SearchInterruptedzHRaised when tools.interrupt.is_interrupted() trips during a search wait.N)Ú__name__Ú
__module__Ú__qualname__Ú__doc__© ó    ú>/home/ubuntu/.hermes/hermes-agent/plugins/web/ddgs/provider.pyr   r   "   s   „ ÚRr   r   c                ó¬  — ddl m} g } |d¬«      5 }t        |j                  | |¬«      «      D ]�  \  }}||k\  r nƒ|j	                  t        t        |j                  dd«      «      t        |j                  d«      xs |j                  d	«      xs d«      t        |j                  d
d«      «      |dz   «      «       Œ� ddd«       |S # 1 sw Y   |S xY w)u  Blocking ddgs query â†’ normalized hits (module-level: the child worker imports it,
    tests patch it for in-process runs).

    ``DDGS(timeout=â€¦)`` bounds each individual HTTP request; the overall wall-clock cap is enforced by the
    parent via process timeout (#68096).
    r   )ÚDDGSé
   ©Útimeout)Úmax_resultsÚtitleÚ ÚhrefÚurlÚbodyé   N)Úddgsr   Ú	enumerateÚtextÚappendr   ÚstrÚget)ÚqueryÚ
safe_limitr   ÚresultsÚclientÚiÚhits          r   Ú_run_ddgs_searchr1   &   só   € õ Ø$&€GÙ	�bÔ	ð P˜VÜ §¡¨E¸z Ó JÓKò 	P‰FˆAˆsØ�JŠÙØ�N‰Nœ9¤S¨¯©°¸"Ó)=Ó%>ÄÀCÇGÁGÈFÃOÒDkÐWZ×W^ÑW^Ð_dÓWeÒDkÐikÓ@lÔnqÐru×ryÑryð  {Að  CEó  sFó  oGð  IJð  MNñ  INó  Oõ  Pð	P÷Pð
 €N÷Pð
 €Nús   ’B-C	Ã	Cc                 ó¢  — 	 ddl } t        | dd«      x}rYt        j                  j	                  t        j                  j	                  t        j                  j                  |«      «      «      S 	 t        j                  j                  t        j                  j                  t        gt        j                  gdz  ¢­Ž «      S # t        $ r Y Œ^w xY w)z•``sys.path`` entry that makes ``import plugins`` work in the child (live package
    location first; correct for source checkouts and site-packages).r   NÚ__file__é   )
ÚpluginsÚgetattrÚosÚpathÚdirnameÚabspathÚ	ExceptionÚjoinr3   Úpardir)Úplugins_pkgÚpkg_files     r   Ú_plugins_path_entryr@   7   s•   € ðÛ%Ü˜{¨J¸Ó=Ð=ˆ8Ð=Ü—7‘7—?‘?¤2§7¡7§?¡?´2·7±7·?±?À8Ó3LÓ#MÓNÐNð >ô �7‰7�?‰?œ2Ÿ7™7Ÿ<™<¬ÐE´R·Y±Y°KÀ!±OÒEÓFÐFøô ò Ùðús   ‚A+C Ã	CÃC)Úgracec               ó:  ‡ ‡— ‰ €yd}dˆˆ fd„}	 ‰ j                   ‰ j                  fD ]"  }‰ j                  «       �Œ |«         |«        }Œ$ |r!t        j	                  d‰ j
                  «       yy# t        $ r }t        j                  d|«       Y d}~yd}~ww xY w)ué   Terminate a worker, escalate to kill, and wait so no orphan remains. Does not close
    the parent's pipe ends â€” closing stdout while another thread is blocked in ``read()``
    deadlocks on some platforms; the caller drains first.NFc                 ó  •— t        j                  «       ‰z   } ‰j                  «       €Tt        j                  «       | k  r=t        j                  d«       ‰j                  «       €t        j                  «       | k  rŒ=‰j                  «       d uS )Ngš™™™™™©?)ÚtimeÚ	monotonicÚpollÚsleep)ÚdeadlinerA   Úprocs    €€r   Ú_wait_until_deadz-_terminate_and_reap.<locals>._wait_until_deadK   sg   ø€ Ü—>‘>Ó# eÑ+ˆØ�i‰i‹kÐ!¤d§n¡nÓ&6¸Ò&AÜ�J‰J�tÔð �i‰i‹kÐ!¤d§n¡nÓ&6¸Ó&Aà�y‰y‹{ $Ð&Ð&r   z*DDGS worker pid=%s did not exit after killzDDGS worker reap error: %s©ÚreturnÚbool)Ú	terminateÚkillrF   ÚloggerÚwarningÚpidr;   Údebug)rI   rA   ÚaliverJ   ÚescalateÚexcs   ``    r   Ú_terminate_and_reaprW   C   s“   ù€ ð €|ØØ€Eö'ð8ØŸ™¨¯©Ð3ò 	/ˆHØ�y‰y‹{Ñ"Ù”
Ù,Ó.Ð.‘ð	/ñ Ü�N‰NÐGÈÏÉÕRð øäò 8Ü�‰Ð1°3×7Ñ7ûð8ús   �+A1 ¼3A1 Á1	BÁ:BÂBc                óx  — | j                  dd«      }t        «       }|r>||j                  t        j                  «      vr|r|t        j                  z   |z   n|| d<   t        j
                  j                  t        j
                  j                  t        j
                  j                  t        «      «      d«      }t        j                  dk(  rdt        j                  inddi}t        j                  t        j                  |gft        j                   t        j                   t        j"                  | ddd	d
œ|¤ŽS )zèStart ``_search_worker.py`` as a script with ``plugins`` importable. Running as a
    script puts ``plugins/web/ddgs/`` on ``sys.path[0]``, breaking ``import plugins...``,
    so the real package location is prepended to PYTHONPATH.Ú
PYTHONPATHr    z_search_worker.pyÚwin32ÚcreationflagsÚstart_new_sessionTzutf-8Úreplace)ÚstdinÚstdoutÚstderrÚenvr'   ÚencodingÚerrors)r*   r@   Úsplitr7   Úpathsepr8   r<   r9   r:   r3   ÚsysÚplatformÚ
subprocessÚCREATE_NEW_PROCESS_GROUPÚPopenÚ
executableÚPIPEÚDEVNULL)ra   Úchild_pythonpathÚ
path_entryÚworker_pathÚextra_kwargss        r   Ú_spawn_workerrr   \   s  € ð —w‘w˜|¨RÓ0ÐÜ$Ó&€JÙ�jÐ(8×(>Ñ(>¼r¿z¹zÓ(JÑJÙJZ˜J¬¯©Ñ3Ð6FÒFÐ`jˆˆLÑÜ—'‘'—,‘,œrŸw™wŸ™¬r¯w©w¯©¼xÓ/HÓIÐK^Ó_€Kä]`×]iÑ]iÐmtÒ]t O´Z×5XÑ5XÑ#Yð  |Oð  QUð  {V€Lô ×ÑÜ	�‰˜Ð%ðÜ-7¯_©_ÄZÇ_Á_Ô]g×]oÑ]oØ�d W°YñàBNñð r   c                óö  — | j                  «       } | st        d|j                  «       › d�«      ‚	 t        j                  | «      }t        |t        «      st        d|›�«      ‚|j                  d«      s't        t        |j                  d«      xs d	«      «      ‚|j                  d
«      xs g }t        |t        «      st        d«      ‚|S # t        j
                  $ r}t        d| dd ›�«      |‚d}~ww xY w)zSDecode the worker's stdout envelope; raise ``RuntimeError`` on any malformed shape.z*DDGS worker exited without a result (code=ú)z#DDGS worker returned invalid JSON: NéÈ   z*DDGS worker returned an invalid envelope: ÚokÚerrorzDDGS worker failedr-   z%DDGS worker returned non-list results)ÚstripÚRuntimeErrorrF   ÚjsonÚloadsÚJSONDecodeErrorÚ
isinstanceÚdictr*   r)   Úlist)ÚrawrI   ÚenveloperV   r-   s        r   Ú_parse_enveloper‚   o   sí   € à
�)‰)‹+€CÙÜÐGÈÏ	É	ËÀ}ÐTUÐVÓWÐWðYÜ—:‘:˜c“?ˆô �h¤Ô%ÜÐGÈÀ|ÐTÓUÐUØ�<‰<˜ÔÜœ3˜xŸ|™|¨GÓ4ÒLÐ8LÓMÓNÐNØ�l‰l˜9Ó%Ò+¨€GÜ�gœtÔ$ÜÐBÓCÐCØ€Nøô ×Ñò YÜÐ@ÀÀTÀcÀÀÐNÓOÐUXÐXûðYús   ±C ÃC8Ã!C3Ã3C8c                óz  — ddl m} ddlm} | |dœ} |t	        t
        j                  «      «      }t        rt        |d<   d|d<   t        |«      x}a	t        j                  d¬	«      }|j                  |j                  t        j                  |«      «      }d
\  }	}
}	 t!        j"                  «       t$        z   }|
sw |«       x}	sn|t!        j"                  «       z
  x}dkD  rR	 |j'                  t)        t*        |«      ¬«      d   xs dd}
}|
s& |«       x}	s|t!        j"                  «       z
  x}dkD  rŒRt/        |«       |j1                  «       s"	 |xs |j'                  t2        ¬«      d   xs d}|j7                  dd¬«       |	rt9        d«      ‚|
st-        dt$        › d�«      ‚t;        ||«      S # t        j,                  $ r Y Œ»w xY w# t4        $ r Y Œew xY w# t/        |«       |j1                  «       s2	 |xs |j'                  t2        ¬«      d   xs d}n# t4        $ r Y nw xY w|j7                  dd¬«       w xY w)u8  Run ``_run_ddgs_search`` in a disposable process with a hard deadline. The parent
    never joins a child that may be in native code holding *its* GIL â€” it polls a
    communicator thread and, on timeout/interrupt, kills the OS process.
    Raises ``TimeoutError``, ``_SearchInterrupted``, or ``RuntimeError``.r   )Úis_interrupted)Ú_sanitize_subprocess_env)r+   r,   Ú	test_hookÚ1ÚHERMES_DDGS_ALLOW_TEST_HOOKSr$   )Úmax_workers)FFr    r   r    TF)ÚwaitÚcancel_futuresúDuckDuckGo search interruptedú"DuckDuckGo search timed out after Ús)Útools.interruptr„   Útools.environments.localr…   r~   r7   Úenvironr   rr   r   ÚcfÚThreadPoolExecutorÚsubmitÚcommunicaterz   ÚdumpsrD   rE   Ú_SEARCH_TIMEOUT_SECSÚresultÚminÚ_POLL_INTERVAL_SECSÚTimeoutErrorrW   ÚdoneÚ_TERMINATE_GRACE_SECSr;   Úshutdownr   r‚   )r+   r,   r„   r…   Úrequestra   rI   ÚpoolÚfutÚinterruptedrœ   r€   rH   Ú	remainings                 r   Ú_run_ddgs_search_boundedr¤   ‚   s4  € õ
 /ÝAà(-¸ZÑH€GÙ
"¤4¬¯
©
Ó#3Ó
4€CÝÜ)ˆ�ÑØ.1ˆÐ*Ñ+Ü,¨SÓ1Ð1€DÐô × Ñ ¨QÔ/€DØ
�+‰+�d×&Ñ&¬¯
©
°7Ó(;Ó
<€CØ-Ñ€K��sð7Ü—>‘>Ó#Ô&:Ñ:ˆÙ©~Ó/?Ð ? Ð ?ÐS[Ô^b×^lÑ^lÓ^nÑSnÐFnÀiÐrsÒEsðØŸJ™J¬sÔ3FÈ	Ó/R˜JÓSÐTUÑVÒ\ÐZ\Ð^b�T�ñ ©~Ó/?Ð ? Ð ?ÐS[Ô^b×^lÑ^lÓ^nÑSnÐFnÀiÐrsÓEsô 	˜DÔ!à�x‰xŒzðØÒO˜SŸZ™ZÔ0E˜ZÓFÀqÑIÒOÈR�ð 	�‰˜5°ˆÔ6ÙÜ Ð!@ÓAÐAÙÜÐ?Ô@TÐ?UÐUVÐWÓXÐXÜ˜3 Ó%Ð%øô —?‘?ò Ùðûô ò Ùðûô 	˜DÔ!à�x‰xŒzðØÒO˜SŸZ™ZÔ0E˜ZÓFÀqÑIÒOÈR‘øÜò Ùðúà�‰˜5°ˆÕ6úsg   ÂAG Ã!)F0 Ä
'G Å!G	 Æ0GÇG ÇGÇG Ç		GÇGÇH:Ç5!HÈH:È	H#È H:È"H#È#H:c                  ó2   — e Zd ZdZdZdZdd„Zd	d
d„Zdd„Zy)ÚDDGSWebSearchProviderz¦DuckDuckGo HTML-scrape search provider (no API key; DDG rate-limits server-side).
    ddgs errors surface as ``{"success": False, "error": ...}`` rather than raising.r%   úDuckDuckGo (ddgs)c                ó,   — 	 ddl }y# t        $ r Y yw xY w)u‰   True when ``ddgs`` is importable. Must NOT do network I/O â€” runs at
        tool-registration time and on every ``hermes tools`` paint.r   NTF)r%   ÚImportError)Úselfr%   s     r   Úis_availablez"DDGSWebSearchProvider.is_available³   s   € ð	ÛØøÜò 	Ùð	ús   ‚ ‡	’c           	     ó
  — | j                  «       st        d«      S 	 t        |t        dt	        |«      «      «      }t        j                  d|t        |«      |«       t        |«      S # t
        $ r1 t        j                  dt        |«       t        dt        › d�«      cY S t        $ r$ t        j                  d|«       t        d«      cY S t        $ r.}t        j                  d|«       t        d	|› �«      cY d
}~S d
}~ww xY w)z³Run the search in a disposable child with a hard wall-clock timeout so a
        hung native ``primp`` call cannot freeze the Hermes process.

        See #36776, #68096.
        u8   ddgs package is not installed â€” run `pip install ddgs`r$   z-DDGS search timed out after %ds for query: %rr�   uh   s â€” DuckDuckGo may be rate-limiting or slow. Try again later or switch to a different search provider.z%DDGS search interrupted for query: %rrŒ   zDDGS search error: %szDuckDuckGo search failed: Nz'DDGS search '%s': %d results (limit %d))r«   r   r¤   ÚmaxÚintr›   rP   rQ   r—   r   Úinfor;   Úlenr	   )rª   r+   ÚlimitÚweb_resultsrV   s        r   ÚsearchzDDGSWebSearchProvider.search¼   sù   € ð × Ñ Ô"ÜÐYÓZÐZð	Cä2°5¼#¸aÄÀUÃÓ:LÓMˆKô 	�‰Ð=¸uÄcÈ+ÓFVÐX]Ô^Ü˜Ó%Ð%øô ò 	Ü�N‰NÐJÔL`ÐbgÔhÜØ4Ô5IÐ4Jð Kuð uóò ô "ò 	@Ü�K‰KÐ?ÀÔGÜÐ>Ó?Ò?Üò 	CÜ�N‰NÐ2°CÔ8ÜÐ!;¸C¸5ÐAÓBÕBûð	Cús)   �A( Á(7DÂ!)DÃDÃ#C=Ã7DÃ=Dc                ó    — t        dddd¬«      S )Nr§   u   free Â· no key Â· search onlyuR   Search via the ddgs Python package â€” no API key (pair with any extract provider)r%   )Ú
post_setup)r
   )rª   s    r   Úget_setup_schemaz&DDGSWebSearchProvider.get_setup_schemaÖ   s   € äØÐ!@Ø`Ðmsô
ð 	
r   NrK   )é   )r+   r)   r±   r®   rL   úDict[str, Any])rL   r¸   )	r   r   r   r   ÚNAMEÚDISPLAY_NAMEr«   r³   r¶   r   r   r   r¦   r¦   ¬   s#   „ ñXð €DØ&€Lóô&ô4
r   r¦   ÚWebSearchProvider)zagent.web_search_providerr»   c                óÌ   — t         j                  | «      }|€t        dt        ›d| ›�«      ‚dd l}ddlm}  |t        | g|¢­Ž  t        |j                  |d   «      |d   «      S )Nzmodule z has no attribute r   )Ú	warn_oncer$   )	Ú_PLUGIN_COMPAT_LAZYr*   ÚAttributeErrorr   Ú	importlibÚhermes_cli.plugin_compatr½   r6   Úimport_module)ÚnameÚtargetrÀ   r½   s       r   Ú__getattr__rÅ   é   se   € Ü ×$Ñ$ TÓ*€FØ€~Ü˜w¤x lÐ2DÀTÀHÐMÓNÐNÛÝ2ÙŒh˜Ð&˜vÓ&Ü�9×*Ñ*¨6°!©9Ó5°v¸a±yÓAÐAr   )r+   r)   r,   r®   rL   úlist[dict[str, Any]])rL   r)   )rI   r   rA   ÚfloatrL   ÚNone)ra   zdict[str, str]rL   úsubprocess.Popen)r€   r)   rI   rÉ   rL   rÆ   )*r   Ú
__future__r   Úconcurrent.futuresÚfuturesr’   rz   Úloggingr7   rh   rf   rD   Útypingr   r   r   Úplugins.web._commonr   r   r	   r
   r   Ú	getLoggerr   rP   r—   rš   r�   r   Ú__annotations__r   r;   r   r1   r@   rW   rr   r‚   r¤   r¦   r¾   rÅ   r   r   r   ú<module>rÒ      sÅ   ðòõ #å Û Û Û 	Û Û 
Û ß &Ñ &ç fÕ fà	ˆ×	Ñ	˜8Ó	$€ð Ð ØÐ ØÐ Ø €
ˆMÓ  Ø04Ð Ð-Ó 4ôS˜ô Sóó"	Gð Mbõ 8ó2ó&ó&'&ôT/
Ð1ô /
ðr ÐKðÐ ó
Br   