
    PL
j                       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dS )+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_secondsreturnOptional[int]c                    	 ddl } |                     | 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	}|
                    t          j                                                              j        }t          |t          z            S # t          $ r Y dS 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       :/home/kuhnn/.hermes/hermes-agent/gateway/memory_monitor.py_get_rss_mbr    4   s    	##H$899C<8##v,---6D=!!!   nnRY[[))5577;3%&&&   tts+   A	A A 
A+*A+/AC 
CC prefixstrNonec                   t                      }t          r(t          t          j                    t          z
            nd}	 t          j                    }n# t          $ r d}Y nw xY w	 t          j	                    }n# t          $ r d}Y nw xY w| r|  dnd}| t                              d||||           dS t                              d|||||           dS )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    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)r"   r   uptime	gc_countsthread_counttags         r   log_memory_usager3   S   s    --C4?FS!!K/000QFLNN		   			 -//    !
(V,,,,bC
{D	
 	
 	
 	
 	
 	=	
 	
 	
 	
 	
s$   A A$#A$(A< <B
B
stop_eventthreading.Eventintervalc                    |                      |          sY	 t                       n2# t          $ r%}t                              d|           Y d}~nd}~ww xY w|                      |          WdS dS )uH   Background thread body — log every ``interval`` seconds until stopped.z#Memory monitor iteration failed: %sN)waitr3   r   r-   debug)r4   r6   es      r   _monitor_loopr;      s    ooh'' C	C 	C 	C 	CLL>BBBBBBBB	C ooh'' C C C C Cs   & 
AAAinterval_secondsboolc                x   t           5  t          't                                          r	 ddd           dS t                      (t                              d           	 ddd           dS t          j                    at          |           a
t          j                    at          d           t          j        t           t          t          fdd          at                                           t                              d	t'          t                               	 ddd           dS # 1 swxY w Y   dS )
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   r3   Threadr;   startr.   r   )r<   s    r   start_memory_monitoringrK      s   * 
 ! !&?+C+C+E+E&! ! ! ! ! ! ! ! == NNS   ! ! ! ! ! ! ! ! n&&!"233o'' 	
++++#* 01)	
 
 
 	I!""	
 	
 	
 C! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !s   "D/)D/-B5D//D36D3       @timeoutc                   t           5  t          t          	 ddd           dS 	 t          d           n# t          $ r Y nw xY wt                                           t          }dadaddd           n# 1 swxY w Y   	 |                    |            n# t          $ r Y nw xY wt                              d           dS )zStop the monitor thread and log a final snapshot.

    Safe to call even if ``start_memory_monitoring()`` was never called.
    Nshutdownr@   )rM   z+[MEMORY] Periodic memory monitoring stopped)	rE   r   r   r3   r   setjoinr-   r.   )rM   threads     r   stop_memory_monitoringrS      sF    
  /"9       
	J///// 	 	 	D	 	                G$$$$    KK=>>>>>sC   A77A7
AA7A'A77A;>A;B 
B'&B'c                     t           5  t          duot                                          cddd           S # 1 swxY w Y   dS )z/True if the background monitor thread is alive.N)rE   r   rF        r   
is_runningrW      s    	 J Jd*I/G/G/I/IJ J J J J J J J J J J J J J J J J Js   "7;;)r   r   )r!   )r"   r#   r   r$   )r4   r5   r6   r	   r   r$   )r   )r<   r	   r   r=   )rL   )rM   r	   r   r$   )r   r=   )__doc__
__future__r   r)   loggingr   r   r+   r'   typingr   	getLogger__name__r-   r   r   __annotations__r   r   r
   LockrE   r    r3   r;   rK   rS   rW   rU   rV   r   <module>r`      s{    < # " " " " " 				  				 



           		8	$	$.2 2 2 2 2)- - - - -# # # # #          	   >+
 +
 +
 +
 +
\C C C C6 6 6 6 6r? ? ? ? ?>J J J J J JrV   