
    `gj                    
   U d 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
  ej                  e      ZdZdaded<   daded	<   dad
ed<   daded<    ej(                         ZddZdddZddZdddZdddZddZy)ue  Periodic process memory usage logging for the gateway.

Ported from cline/cline#10343 (src/standalone/memory-monitor.ts).

The gateway is a long-lived process that accumulates memory as it caches
agent instances, session transcripts, tool schemas, memory providers, MCP
connections, etc.  A slow leak in any of those subsystems is invisible
in a single log line — you only see it by watching RSS climb over hours.

This module emits a single structured ``[MEMORY] ...`` line every N
minutes (default 5) so maintainers investigating a suspected leak can
grep ``agent.log`` / ``gateway.log`` for a time series of RSS + Python
GC stats.  The timer runs in a background thread and shuts down cleanly
with the gateway.

Design notes (parity with the Cline port):
  * Grep-friendly single-line format beginning ``[MEMORY]``.
  * Final snapshot logged on shutdown so "last RSS before exit" is
    always in the log.
  * Baseline snapshot logged immediately on start.
  * Daemon thread — never blocks process exit.
  * Uses ``resource`` (stdlib, Linux/macOS) first and falls back to
    ``psutil`` when ``resource`` isn't available (Windows).  Both are
    optional; when neither works we emit a single WARNING and disable
    the monitor rather than crashing the gateway.

Config: ``logging.memory_monitor`` in ``config.yaml`` — see
``hermes_cli/config.py`` for the defaults block.
    )annotationsN)Optionali   zOptional[threading.Thread]_monitor_threadzOptional[threading.Event]_stop_eventzOptional[float]_start_time     r@float_interval_secondsc                    	 ddl } | j                  | j                        j                  }t        j
                  dk(  rt        |t        z        S t        |dz        S # t        $ r Y nw xY w	 ddl	}|j                  t        j                               j                         j                  }t        |t        z        S # t        $ r Y yw xY w)zReturn current process resident set size in MB, or None if unavailable.

    Tries ``resource.getrusage`` first (Linux/macOS, no extra deps), then
    falls back to ``psutil`` which is an optional hermes-agent dep.
    r   Ndarwini   )resource	getrusageRUSAGE_SELF	ru_maxrsssysplatformint_BYTES_TO_MB	ExceptionpsutilProcessosgetpidmemory_inforss)r   maxrssr   r   s       I/root/.hermes/venv/lib/python3.12/site-packages/gateway/memory_monitor.py_get_rss_mbr   4   s    	##H$8$89CC<<8#v,--6D=!! nnRYY[)557;;3%&& s+   AA A 	A*)A*.AB? ?	C
Cc                   t               }t        r$t        t        j                         t        z
        nd}	 t        j                         }	 t        j                         }| r|  dnd}|t        j                  d||||       yt        j                  d|||||       y# t        $ r d}Y bw xY w# t        $ r d}Y ^w xY w)u[  Log current memory usage in a grep-friendly ``[MEMORY] ...`` line.

    Safe to call on-demand from any thread at important lifecycle
    moments (after shutdown, after context compression, etc.).

    Parameters
    ----------
    prefix
        Optional extra tag inserted after ``[MEMORY]`` — e.g.
        ``"baseline"``, ``"shutdown"``.
    r   )r   r   r     Nz6[MEMORY] %srss=unavailable gc=%s threads=%d uptime=%dsz/[MEMORY] %srss=%dMB gc=%s threads=%d uptime=%ds)r   r   r   time	monotonicgc	get_countr   	threadingactive_countloggerinfo)prefixr   uptime	gc_countsthread_counttags         r   log_memory_usager/   S   s     -C4?S!K/0QFLLN	 --/ !VHA,bC
{D	
 	=	
%  	
  s#   B! B2 !B/.B/2C ?C c                    | j                  |      s	 t                | j                  |      syy# t        $ r }t        j	                  d|       Y d}~8d}~ww xY w)uH   Background thread body — log every ``interval`` seconds until stopped.z#Memory monitor iteration failed: %sN)waitr/   r   r(   debug)
stop_eventintervales      r   _monitor_loopr6      sN    ooh'	C ooh'  	CLL>BB	Cs   
1 	AAAc                   t         5  t        t        j                         r
	 ddd       yt               t        j                  d       	 ddd       yt        j                         at        |       a
t        j                         at        d       t        j                  t         t        t        fdd      at        j#                          t        j%                  d	t'        t                     	 ddd       y# 1 sw Y   yxY w)
uF  Start periodic memory usage logging in a daemon thread.

    Logs immediately to capture a baseline, then every ``interval_seconds``.
    Safe to call multiple times — subsequent calls are no-ops while the
    first monitor is still running.

    Parameters
    ----------
    interval_seconds
        How often to log.  Default 300s (5 minutes), matching the
        upstream cline/cline implementation.

    Returns
    -------
    bool
        True if a fresh monitor thread was started, False if one was
        already running or if memory introspection isn't available.
    NFu   [MEMORY] Memory monitoring unavailable: neither resource.getrusage nor psutil could read process RSS — skipping periodic logging.baseliner*   zgateway-memory-monitorT)targetargsnamedaemonz;[MEMORY] Periodic memory monitoring started (interval: %ds))_lockr   is_aliver   r(   warningr"   r#   r   r	   r
   r&   Eventr   r/   Threadr6   startr)   r   )interval_secondss    r   start_memory_monitoringrE      s    * 
 !&?+C+C+E! ! = NNS ! ! nn&!"23oo' 	
+#** 01)	
 	I!"	
 C! ! !s   C< C<BC<<Dc                H   t         5  t        t        
	 ddd       y	 t        d       t        j                          t        }dadaddd       	 j                  |        t        j                  d       y# t        $ r Y Zw xY w# 1 sw Y   AxY w# t        $ r Y <w xY w)zStop the monitor thread and log a final snapshot.

    Safe to call even if ``start_memory_monitoring()`` was never called.
    Nshutdownr9   )timeoutz+[MEMORY] Periodic memory monitoring stopped)	r>   r   r   r/   r   setjoinr(   r)   )rH   threads     r   stop_memory_monitoringrL      s     
 /"9 
	J/ 	  G$ KK=>  		 $  s?   B	A:B	B :	BB	BB		B	B! B!c                 v    t         5  t        duxr t        j                         cddd       S # 1 sw Y   yxY w)z/True if the background monitor thread is alive.N)r>   r   r?        r   
is_runningrP      s4    	 Jd*I/G/G/IJ J Js   /8)returnzOptional[int])r!   )r*   strrQ   None)r3   zthreading.Eventr4   r	   rQ   rS   )r   )rD   r	   rQ   bool)g       @)rH   r	   rQ   rS   )rQ   rT   )__doc__
__future__r   r$   loggingr   r   r&   r"   typingr   	getLogger__name__r(   r   r   __annotations__r   r   r
   Lockr>   r   r/   r6   rE   rL   rP   rN   rO   r   <module>r]      s   < # 	  	 
   			8	$.2+ 2)-& -#_ #  5  	>+
\C6r?>JrO   