o
    ưi4y                     @   sH  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 d dlm	Z	m
Z
mZmZmZ d dlmZmZmZmZ d dlmZ d dlmZ d dlmZ d dlmZ d d	lmZ e Zd
d Ze  eddd Z ej!dd" dkrz$d dl#Z#e$d e#%  e$d e#&  e#' Z(e$d e#j&e(d W n e)y   e)dw e*d ejddddd Z+ejdddeefdefdd Z,ejd!ddeefdefd"d#Z-ejd$ddeefded%e
e.e	f fd&d'Z/d%e
e.e	f fd(d)Z0d*e1d%ee1ee
e.e	f  f fd+d,Z2d%e
e.e	f fd-d.Z3d%e
e.e	f fd/d0Z4d%e
e.e	f fd1d2Z5d3e1d4e6d%ee
e.e	f  fd5d6Z7ejd7ddeeed8d9d:ed;d<d:fded*e1d4e6d%e
e.e	f fd=d>Z8ej9d?ddeeed@dAd:eddBd:eddCd:fdedDe1dEe1dFe1d%e
e.e	f f
dGdHZ:ejdIdddJdK Z;dLdM Z<dS )N    N)Counter)AnyDictListOptionalTuple)	APIRouterDependsHTTPExceptionQuery)get_secret_str)verbose_proxy_logger)PYTHON_GC_THRESHOLD)UserAPIKeyAuth)user_api_key_authc               
   C   s   t } | rKz)dd | dD }t|dkr#tj|  td|  n	td|  d W n tyJ } ztd|  d	|  W Y d
}~nd
}~ww t	 }td|d  d|d  d|d   d
S )zJConfigure Python garbage collection thresholds from environment variables.c                 S   s   g | ]}t | qS  )intstrip).0xr   r   ]/home/app/Keep/.python/lib/python3.10/site-packages/litellm/proxy/common_utils/debug_utils.py
<listcomp>       z+configure_gc_thresholds.<locals>.<listcomp>,   zGC thresholds set to: zGC threshold not set: z#. Expected format: 'gen0,gen1,gen2'zFailed to parse GC threshold: z	. Error: NzCurrent GC thresholds: gen0=r   z, gen1=   z, gen2=   )
r   splitlengcset_thresholdr   infowarning
ValueErrorget_threshold)Zgc_threshold_env
thresholdseZcurrent_thresholdsr   r   r   configure_gc_thresholds   s   
",r'   z/debug/asyncio-tasksc                     s   d} t  }dd |D }t }t|D ]&\}}|| kr n| }t|ddp2t|ddp2t|}||  d7  < qt|t|dS )	zY
    Returns:
      total_active_tasks: int
      by_name: { coroutine_name: count }
    i  c                 S   s   g | ]}|  s|qS r   )done)r   tr   r   r   r   9   r   z*get_active_tasks_stats.<locals>.<listcomp>__qualname__N__name__r   )Ztotal_active_tasksZby_name)	asyncio	all_tasksr   	enumerateget_corogetattrreprr   dict)ZMAX_TASKS_TO_CHECKr-   Zactive_taskscounteridxtaskcoronamer   r   r   get_active_tasks_stats-   s$   
r8   ZLITELLM_PROFILEfalsetruezgrowth of objectsz

Most common typesz

Leaking objects)objectsz@objgraph not found. Please install objgraph to use this feature.
   z/memory-usageF)Zinclude_in_schemac                     sh   t  } | d}td| |d d }g }|D ]}||jjdd d|jd  d qd	|iS )
NlinenozTOP STATS: %s2   r<   )limitz: i   z KiBZtop_50_memory_usage)	tracemallocZtake_snapshot
statisticsr   debugappend	tracebackformatsize)ZsnapshotZ	top_statsZtop_50resultstatr   r   r   memory_usagec   s   
(rI   z/memory-usage-in-mem-cache_c                    s   ddl m}m}m} |du rd}nt|jjjt|jjj }t|jjt|jj }t|j	j
jjt|j	j
jj }|||dS )j
    1. user_api_key_cache
    2. router_cache
    3. proxy_logging_cache
    4. internal_usage_cache
    r   
llm_routerproxy_logging_objuser_api_key_cacheN)num_items_in_user_api_key_cachenum_items_in_llm_router_cache$num_items_in_proxy_logging_obj_cache)litellm.proxy.proxy_serverrM   rN   rO   r   cachein_memory_cache
cache_dictttl_dictinternal_usage_cache
dual_cache)rJ   rM   rN   rO   rQ   rP   rR   r   r   r   memory_usage_in_mem_caches   s.   

rZ   z /memory-usage-in-mem-cache-itemsc                    sf   ddl m}m}m} |du ri }i }n
|jjj}|jjj}|jj|jj|||jj	jj|jj	jjdS )rK   r   rL   N)rO   Zuser_api_key_ttlllm_router_cacheZllm_router_ttlZproxy_logging_obj_cacheZproxy_logging_obj_ttl)
rS   rM   rN   rO   rT   rU   rV   rW   rX   rY   )rJ   rM   rN   rO   Zllm_router_in_memory_cache_dictZllm_router_in_memory_ttl_dictr   r   r   memory_usage_in_mem_cache_items   s   



r\   z/debug/memory/summaryreturnc              
      s  ddl m}m}m} i }d}z;ddl}| }| }|jd }	| }
|	dd|
ddt	|	d	t	|
d	d
}|
dkr@d}n	|
dkrGd}nd}W n$ t
yW   d|d< Y n tyn } zt||d< W Y d}~nd}~ww i }d}zEt|jj}||7 }||ddd|d< |durt|jjj}||7 }||ddd|d< t|jjjj}||7 }||ddd|d< W n ty } zt||d< W Y d}~nd}~ww t }t d }ttj}|rdnd|d}|dkr| d|d< t ||||d|dS )a'  
    Get simplified memory usage summary for the proxy.
    
    Returns:
    - worker_pid: Process ID
    - status: Overall health based on memory usage
    - memory: Process memory usage and RAM info
    - caches: Cache item counts and descriptions
    - garbage_collector: GC status and pending object counts
    
    Example usage:
    curl http://localhost:4000/debug/memory/summary -H "Authorization: Bearer sk-1234"
    
    For detailed analysis, call GET /debug/memory/details
    For cache management, use the cache management endpoints
    r   rL   ZhealthyN   .1fz MB (% of system memory)r   )summaryram_usage_mbsystem_memory_percentP   critical<   r"   z8Install psutil for memory monitoring: pip install psutilerrorr   z,Validated API keys for faster authentication)countcount_readableZwhat_it_storesZuser_api_keysz$LLM responses for identical requestsZllm_responsesz#Usage metrics before database writeZusage_trackingenableddisabled)statusobjects_awaiting_collectionz- uncollectable objects (possible memory leak))Ztotal_itemsZ	breakdown)
worker_pidrl   Zmemorycachesgarbage_collector)rS   rM   rN   rO   psutilProcessmemory_inforssmemory_percentroundImportError	Exceptionstrr   rU   rV   rT   rX   rY   r   	isenabled	get_countgarbageosgetpid)rJ   rM   rN   rO   process_memoryZhealth_statusrq   processrs   Z	memory_mbru   r&   ro   Ztotal_cache_itemsZuser_cache_itemsZrouter_cache_itemsZlogging_cache_itemsZ
gc_enabledZobjects_pendinguncollectableZgc_infor   r   r   get_memory_summary   s   





r   c                   C   sh   t  t  d t  d t  d ddt  d t  d t  d dddd tt  D d	S )
z!Get garbage collector statistics.r   r   r   zENumber of allocations before automatic collection for each generation)generation_0generation_1generation_2Zexplanationz6Current number of allocated objects in each generationc                 S   s*   g | ]\}}||d  |d |d dqS )collections	collectedr   )Z
generationZtotal_collectionsZtotal_collectedr   r   )r   irH   r   r   r   r   E  s    z&_get_gc_statistics.<locals>.<listcomp>)rj   r%   Zcurrent_countsZcollection_history)r   rz   r$   r{   r.   Z	get_statsr   r   r   r   _get_gc_statistics5  s   






r   top_nc                 C   sV   t  }d}t D ]}|d7 }t|j}||  d7  < q	dd || D }||fS )z=Count objects by type and return total count and top N types.r   r   c                 S   s    g | ]\}}|||d dqS )r   )typerh   ri   r   )r   obj_typerh   r   r   r   r   [  s    z+_get_object_type_counts.<locals>.<listcomp>)r   r   Zget_objectsr   r+   most_common)r   Ztype_countstotal_objectsobjr   top_object_typesr   r   r   _get_object_type_countsQ  s   
	r   c                  C   s<   t j} t| dd | dd D t| dkrddS ddS )zEGet information about uncollectable objects (potential memory leaks).c                 S   s   g | ]}t |jqS r   )r   r+   )r   r   r   r   r   r   l  s    z3_get_uncollectable_objects_info.<locals>.<listcomp>Nr<   r   zIIf count > 0, you may have reference cycles preventing garbage collection)rh   Zsample_typesr"   )r   r|   r   )r   r   r   r   _get_uncollectable_objects_infog  s   r   c              
   C   s  i }zt | jj}t | jj}t| jj||t|| d dd|d< |durNt |jjj}t |jjj}t|jjj||t|| d dd|d< t |jj	jj}	t |jj	jj}
t|jj	jj|	|
t|	|
 d dd|d< |durdt
|jd	|d
< z<t|dr|jrt|jdr|jj}t|dr|jndt|dr|jjndd|d
 d< W W |S W W |S W W |S W W |S  ty } ztd|  W Y d}~W |S d}~ww ddi|d
< W |S  ty } ztd|  t||d< W Y d}~|S d}~ww )z&Calculate memory usage for all caches.r^   r   )	num_itemsZcache_dict_size_bytesZttl_dict_size_bytesZtotal_size_mbrO   Nr[   Zproxy_logging_cacheT)rj   Z
cache_typeredis_usage_cacheredis_clientconnection_poolmax_connectionsconnection_class)r   r   zError getting Redis pool info: rj   FzError calculating cache stats: rg   )sys	getsizeofrU   rV   rW   r   rv   rT   rX   rY   r   r+   hasattrr   r   r   r   rx   r   rB   ry   )rO   rM   rN   r   cache_statsZuser_cache_sizeZuser_ttl_sizeZrouter_cache_sizeZrouter_ttl_sizeZlogging_cache_sizeZlogging_ttl_sizeZ	pool_infor&   r   r   r   _get_cache_memory_statsq  sz   








r   c           	   
   C   s  i }z| durt | dr%| jr%t| j}t| j|t|d dd|d< t | drC| jrCt| j}t| j|t|d dd|d< t | d	ra| jrat| j}t| j|t|d dd
|d	< t | dr| jrt| j}t| j|t|d dd|d< t | dr| j	rt| j	}t| j	|t|d dd|d< t| }|t|d dd|d< W |S ddi}W |S  t
y } ztd|  dt|i}W Y d}~|S d}~ww )z/Get memory usage statistics for LiteLLM router.N
model_listr^      )Z
num_models
size_bytessize_mbmodel_names)Znum_model_groupsr   r   Zmodel_names_setdeployment_names)Znum_deploymentsr   r   deployment_latency_map)Znum_tracked_deploymentsr   r   	fallbacks)Znum_fallback_configsr   r   )r   r   Zrouter_objectZnotezRouter not initializedz"Error getting router memory info: rg   )r   r   r   r   r   rv   r   r   r   r   rx   r   rB   ry   )	rM   litellm_router_memoryZmodel_list_sizeZmodel_names_sizeZdeployment_names_sizeZlatency_map_sizeZfallbacks_sizeZrouter_obj_sizer&   r   r   r   _get_router_memory_stats  s\   






r   rn   include_process_infoc           	   
   C   s  |sdS zTddl }| }| }t|jd d}t|jd d}t| d}| d|  d|dd|dd	|d
d|dd|ddt|drK| nddd|	 dddW S  t
yf   | dd Y S  ty } ztd|  | t|dW  Y d}~S d}~ww )z2Get process-level memory information using psutil.Nr   r^   r   zWorker PID z using r_   z MB of RAM (r`   z(Actual physical RAM used by this process)Z	megabytesdescriptionz8Total virtual memory allocated (includes swapped memory)z)Percentage of total system RAM being used)percentr   num_fdszN/A (Windows)z'Number of open file descriptors/handles)rh   r   z(Number of active threads in this process)pidra   Z	ram_usageZvirtual_memoryrc   Zopen_file_handlesthreadsz6psutil not installed. Install with: pip install psutil)r   rg   zError getting process info: )rq   rr   rs   rv   rt   Zvmsru   r   r   Znum_threadsrw   rx   r   rB   ry   )	rn   r   rq   r   rs   rb   Zvirtual_memory_mbru   r&   r   r   r   _get_process_memory_info  sH   
r   z/debug/memory/details   z$Number of top object types to return)r   TzInclude process memory infoc                    sz   ddl m}m}m}m} t }t }t|\}	}
t	 }t
||||}t|}t||}||||	|	d|
d|||dS )a  
    Get detailed memory diagnostics for deep debugging.
    
    Returns:
    - worker_pid: Process ID
    - process_memory: RAM usage, virtual memory, file handles, threads
    - garbage_collector: GC thresholds, counts, collection history
    - objects: Total tracked objects and top object types
    - uncollectable: Objects that can't be garbage collected (potential leaks)
    - cache_memory: Memory usage of user_api_key, router, and logging caches
    - router_memory: Memory usage of router components (model_list, deployment_names, etc.)
    
    Query Parameters:
    - top_n: Number of top object types to return (default: 20)
    - include_process_info: Include process-level memory info using psutil (default: true)
    
    Example usage:
    curl "http://localhost:4000/debug/memory/details?top_n=30" -H "Authorization: Bearer sk-1234"
    
    All memory sizes are reported in both bytes and MB.
    r   )rM   rN   rO   r   r   )Ztotal_trackedZtotal_tracked_readableZ	top_types)rn   r   rp   r;   r   Zcache_memoryZrouter_memory)rS   rM   rN   rO   r   r}   r~   r   r   r   r   r   r   )rJ   r   r   rM   rN   rO   r   rn   Zgc_statsr   r   Zuncollectable_infor   r   Zprocess_infor   r   r   get_memory_details'  s(   
r   z/debug/memory/gc/configurei  z%Generation 0 threshold (default: 700)z$Generation 1 threshold (default: 10)z$Generation 2 threshold (default: 10)r   r   r   c                    s   t  }zt ||| td| d| d| d| d	 W n ty? } ztd|  tddt| dd}~ww t 	 d	 }d
|d	  d|d  d|d  | d| d| |d||  ddS )a  
    Configure Python garbage collection thresholds.
    
    Lower thresholds mean more frequent GC cycles (less memory, more CPU overhead).
    Higher thresholds mean less frequent GC cycles (more memory, less CPU overhead).
    
    Returns:
    - message: Confirmation message
    - previous_thresholds: Old threshold values
    - new_thresholds: New threshold values
    - objects_awaiting_collection: Current object count in gen-0
    - tip: Hint about when next collection will occur
    
    Query Parameters:
    - generation_0: Number of allocations before gen-0 collection (default: 700)
    - generation_1: Number of gen-0 collections before gen-1 collection (default: 10)  
    - generation_2: Number of gen-1 collections before gen-2 collection (default: 10)
    
    Example for more aggressive collection:
    curl -X POST "http://localhost:4000/debug/memory/gc/configure?generation_0=500" -H "Authorization: Bearer sk-1234"
    
    Example for less aggressive collection:
    curl -X POST "http://localhost:4000/debug/memory/gc/configure?generation_0=1000" -H "Authorization: Bearer sk-1234"
    
    Monitor memory usage with GET /debug/memory/summary after changes.
    zGC thresholds updated from z to (z, )zFailed to set GC thresholds: i  )status_codedetailNr   zGC thresholds updatedr   r   zNext collection will run after z more allocations)messageZprevious_thresholdsZnew_thresholdsrm   Ztip)
r   r$   r    r   r!   rx   rg   r
   ry   r{   )rJ   r   r   r   Zold_thresholdsr&   Zcurrent_countr   r   r    configure_gc_thresholds_endpointb  s8   "r   z/otel-spansc            	         s   ddl m}  | d u rg i d dS | j}t|dr| }ng }td| d }d}i }|D ]%}|jd urR|jj}||vr@g ||< || |j	 |j
|krR|}|j
}q-dd |D }|||dS )	Nr   )open_telemetry_logger)Z
otel_spansspans_grouped_by_parentmost_recent_parentget_finished_spanszSpans: i@B c                 S   s   g | ]}|j qS r   )r7   )r   spanr   r   r   r     s    z"get_otel_spans.<locals>.<listcomp>)rS   r   ZOTEL_EXPORTERr   r   printparentZtrace_idrC   r7   
start_time)	r   Zotel_exporterZrecorded_spansr   Zmost_recent_start_timer   r   Zparent_trace_idZ
span_namesr   r   r   get_otel_spans  s<   




r   c            
   
   C   s  zt d} | d u rW d S tj| rW d S t| }t|ts"W d S |dd }|dd }|du rUdd l	}ddl
m}m}m} |j|jd |j|jd |j|jd |du rdd l	}ddl
m}m}m} |j|jd |j|jd |j|jd W d S |du r|du rtjd	d
}|d ur| dkrdd l	}ddl
m}m} |j|jd |j|jd W d S | dkrdd l	}ddl
m}m} |j|jd |j|jd W d S W d S W d S W d S W d S  ty }	 zdd l	}|dt|	  W Y d }	~	d S d }	~	ww )NZWORKER_CONFIGrB   detailed_debugTr   )verbose_loggerr   verbose_router_logger)levelFZLITELLM_LOG INFO)r   r   DEBUGz Failed to init verbose loggers: )r   r}   pathisfilejsonloads
isinstancer2   getlogginglitellm._loggingr   r   r   setLevelr   r   environupperrx   r"   ry   )
Zworker_configZ	_settingsrB   r   r   r   r   r   Zlitellm_log_settingr&   r   r   r   init_verbose_loggers  st   

"r   )=r,   r   r   r}   r   r@   r   r   typingr   r   r   r   r   Zfastapir   r	   r
   r   Zlitellmr   r   r   Zlitellm.constantsr   Zlitellm.proxy._typesr   Z$litellm.proxy.auth.user_api_key_authr   Zrouterr'   r   r8   r   lowerZobjgraphr   Zshow_growthZshow_most_common_typesZget_leaking_objectsrootsrw   startrI   rZ   r\   ry   r   r   r   r   r   r   r   boolr   r   postr   r   r   r   r   r   r   <module>   s   
#

&!
x&
D"B0


:



=
+