
    (<i-                    ^   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mZ ddl	m
Z
  ee      j                         j                  j                  Z ee      ej                   vr"ej                   j#                  d ee             ddlmZ  ee      Zej,                  j/                  dd      Zdd	Z e       Zej/                  d
d      Zej/                  dd      dz  Zej/                  dd      dz  ZdZdZdZ edede diZ! G d d      Z"ddef	 	 	 	 	 	 	 ddZ#ddZ$edk(  r e$        yy)u  
utils/session_monitor.py — 봇 세션 토큰 사용량 실시간 추적 모니터

봇 세션의 토큰 사용량을 실시간으로 추적하고, 임계값(warning/critical)
도달 시 콜백을 호출하며 현재 상태를 반환합니다.

Usage:
    from utils.session_monitor import SessionMonitor

    monitor = SessionMonitor(context_limit=200_000)
    level = monitor.update({"input_tokens": 50_000, "output_tokens": 30_000})
    status = monitor.get_usage_status()

CLI:
    python3 utils/session_monitor.py --status
    )annotationsN)Path)Callable)
get_loggerWORKSPACE_ROOTz/home/jay/workspacec                    t        t              dz  dz  } 	 t        | d      5 }t        j                  |      }ddd       j                  di       S # 1 sw Y   xY w# t        t        j                  t        f$ r i cY S w xY w)u   constants.json에서 session_monitoring 설정을 로드한다.

    로드 실패 시 빈 dict 반환 (호출측에서 fallback 사용).
    configzconstants.jsonutf-8encodingNsession_monitoring)	r   _WORKSPACE_ROOTopenjsonloadgetFileNotFoundErrorJSONDecodeErrorKeyError)config_pathfdatas      F/home/jay/workspace/.worktrees/task-2057-dev2/utils/session_monitor.py_load_session_monitoring_configr   (   s    
 '(25EEK+0 	 A99Q<D	 xx,b11	  	  t33X> 	s'   A  AA  AA    BBcontext_limiti@ warning_pctF   d   critical_pctU   normalwarningcritical      c                  `    e Zd ZdZeeef	 	 	 	 	 	 	 d
dZddZddZ	dddZ
ddZddZddZy	)SessionMonitoru2  봇 세션의 토큰 사용량을 실시간으로 추적합니다.

    Args:
        context_limit: 최대 컨텍스트 윈도우 토큰 수 (기본값: 200,000)
        warning_pct: 경고 임계값 비율 (기본값: 0.70 = 70%)
        critical_pct: 위험 임계값 비율 (기본값: 0.85 = 85%)
    c                    || _         || _        || _        d| _        t        | _        t        g t        g i| _        t        j                  d||dz  |dz         y )Nr   uC   SessionMonitor 초기화: limit=%d, warning=%.0f%%, critical=%.0f%%r   )_context_limit_warning_pct_critical_pct_total_tokens_LEVEL_NORMAL_current_level_LEVEL_WARNING_LEVEL_CRITICAL
_callbacksloggerdebug)selfr   r   r   s       r   __init__zSessionMonitor.__init__P   sf     ,')"##0 BRD

 	Q#3		
    c                b   t        |j                  d      xs d      }t        |j                  d      xs d      }| xj                  ||z   z  c_        | j                  | j                        }| j	                  |       || _        t        j                  d||z   ||| j                  |       |S )u  Anthropic API 응답의 usage 필드를 처리하고 누적 카운터를 갱신합니다.

        Args:
            usage: {"input_tokens": N, "output_tokens": M} 형태의 딕셔너리

        Returns:
            현재 레벨 문자열: "normal", "warning", "critical"
        input_tokensr   output_tokensz<update: +%d tokens (input=%d, output=%d), total=%d, level=%s)intr   r,   _compute_level_fire_callbacks_on_transitionr.   r2   r3   )r4   usager8   r9   	new_levels        r   updatezSessionMonitor.updaten   s     599^49:EIIo6;!<l]::''(:(:;	**95'J=(	
 r6   c                    | j                   dkD  r&t        | j                  | j                   z  dz  d      nd}| j                  | j                   || j                  dS )u   현재 세션 토큰 사용 상태를 반환합니다.

        Returns:
            {
                "total_tokens": N,
                "limit": 200000,
                "usage_pct": 75.0,
                "level": "warning"
            }
        r   r   r%   g        )total_tokenslimit	usage_pctlevel)r)   roundr,   r.   )r4   rC   s     r   get_usage_statuszSessionMonitor.get_usage_status   sa     QUPcPcfgPgE$,,t/B/BBSH!Lmp	 ..(("((	
 	
r6   c                    | j                   }|| _         | j                  |      | _        t        j	                  d||| j                         y)u   토큰 카운터를 리셋합니다. 압축 후 실제 토큰 수로 설정 가능합니다.

        Args:
            new_total: 리셋 후 설정할 토큰 수 (기본값: 0)
        u1   SessionMonitor 리셋: %d → %d tokens, level=%sN)r,   r;   r.   r2   info)r4   	new_total	old_totals      r   resetzSessionMonitor.reset   sH     &&	&"11)<?		
r6   c                    |j                         }|| j                  vrt        j                  d|       y| j                  |   j	                  |       t        j                  d|       y)u   특정 레벨 도달 시 호출될 콜백을 등록합니다.

        Args:
            level: "warning" 또는 "critical"
            callback: 상태 딕셔너리를 인자로 받는 콜백 함수
        u=   알 수 없는 콜백 레벨: %s (warning/critical만 지원)Nu   콜백 등록: level=%s)lowerr1   r2   r"   appendr3   )r4   rD   callback
normalizeds       r   register_callbackz SessionMonitor.register_callback   sT     [[]
T__,NNZ\ab
#**84.
;r6   c                    | j                   dk  rt        S || j                   z  }|| j                  k\  rt        S || j                  k\  rt
        S t        S )u/   토큰 수에 따른 레벨을 계산합니다.r   )r)   r-   r+   r0   r*   r/   )r4   rA   pcts      r   r;   zSessionMonitor._compute_level   sT    !#  T000$$$$""$###!!r6   c                N   t         j                  | j                  d      }t         j                  |d      }||k  ry| j                         }|| j                  v r| j                  |   D ]  }	  ||        yy# t
        $ r!}t        j                  d||       Y d}~5d}~ww xY w)uC   레벨 전환 시에만 해당 레벨의 콜백을 호출합니다.r   Nu#   콜백 실행 오류 (level=%s): %s)_LEVEL_ORDERr   r.   rF   r1   	Exceptionr2   error)r4   r>   	old_order	new_orderstatuscbexcs          r   r<   z,SessionMonitor._fire_callbacks_on_transition   s     $$T%8%8!<	 $$Y2	 	!&&('ooi0 XXvJX ( ! XLL!F	SVWWXs   .A::	B$BB$N)r   r:   r   floatr   r]   returnNone)r=   dictr^   strr^   r`   )r   )rI   r:   r^   r_   )rD   ra   rO   zCallable[[dict], None]r^   r_   )rA   r:   r^   ra   )r>   ra   r^   r_   )__name__
__module____qualname____doc___DEFAULT_CONTEXT_LIMIT_DEFAULT_WARNING_PCT_DEFAULT_CRITICAL_PCTr5   r?   rF   rK   rQ   r;   r<    r6   r   r'   r'   G   s\     413	

 
 	

 

<8
&
"<$	Xr6   r'   c           
     0   t        t              dz  }| t        |dz        } |t        |dz        }i }	 t        | d      5 }t	        j
                  |      }ddd       j                  di       j                         D ]  \  }}|j                  d      d	k(  s|||<   ! 	 i }
	 t        |d      5 }t	        j
                  |      }ddd       j                  di       }
g }|j                         D ]  \  }}|
j                  |i       }t        |j                  dd            }|j                  d|j                  dd            }t        |      }|j!                  |       |j#                         }|j%                  |||d   |d   |d   |d   d       t        j'                  d|||d   |d           d|iS # 1 sw Y   kxY w# t        $ r t        j                  d
|        Y Mt        j                  t        f$ r!}	t        j                  d|	       Y d}	~	d}	~	ww xY w# 1 sw Y   exY w# t        $ r t        j                  d|       Y xt        j                  t        f$ r!}	t        j                  d|	       Y d}	~	d}	~	ww xY w)u  실행 중인 태스크의 세션 토큰 사용 상태를 반환합니다.

    Args:
        timers_path: task-timers.json 파일 경로 (기본: memory/task-timers.json)
        ledger_path: token-ledger.json 파일 경로 (기본: memory/token-ledger.json)
        context_limit: 컨텍스트 윈도우 한도 (기본: 200,000)

    Returns:
        {
            "sessions": [
                {
                    "task_id": "task-100.1",
                    "team_id": "dev6-team",
                    "total_tokens": 140000,
                    "limit": 200000,
                    "usage_pct": 70.0,
                    "level": "warning"
                },
                ...
            ]
        }
    memoryNztask-timers.jsonztoken-ledger.jsonr
   r   tasksrZ   runningu0   task-timers.json 파일을 찾을 수 없음: %su"   task-timers.json 파싱 오류: %su1   token-ledger.json 파일을 찾을 수 없음: %su#   token-ledger.json 파싱 오류: %srA   r   team_id r   )rI   rB   rC   rD   )task_idro   rA   rB   rC   rD   u7   세션 상태: task=%s, tokens=%d, pct=%.1f%%, level=%ssessions)r   r   ra   r   r   r   r   itemsr   r2   r"   r   r   rW   r:   r'   rK   rF   rN   r3   )timers_pathledger_pathr   
memory_dirrunning_tasksr   timers_datarr   	task_infor\   ledger_tasksledger_datars   ledger_entryrA   ro   monitorrZ   s                     r   get_active_sessions_statusr      s   6 o&1J*'99:*'::; &(M	@+0 	'A))A,K	'"-//'2">"D"D"F 	3GY}}X&)3)2g&	3 %'LA+0 	'A))A,K	'"w3 H+113 
#''4<++NA>?--	<+;+;Ir+JK }=-))+"" &~ 6#K0		
 	E;7O	
'
6 !!e	' 	'
  XI;W  (+ @93??@	' 	'  YJKX  (+ A:C@@Asr   G GAG G 'H: 4H-
H: GG H*1H*	H%%H*-H72H: :JJ4JJc                    t        j                  dd      } | j                  ddd       | j                  dt        t        d	t        d
d       | j                         }|j                  r8t        |j                        }t        t        j                  |dd             y | j                          t        j                  d       y )Nu%   봇 세션 토큰 사용량 모니터session_monitor)descriptionprogz--status
store_trueu3   현재 활성 세션 토큰 사용률 출력 (JSON))actionhelpz--limitu*   컨텍스트 윈도우 한도 (기본값: ,))typedefaultr   rq   Fr%   )ensure_asciiindentr$   )argparseArgumentParseradd_argumentr:   rg   
parse_argsrZ   r   rB   printr   dumps
print_helpsysexit)parserargsresults      r   _mainr   B  s    $$;F B  
 &9:PQR9SSTU	   D{{+$**EdjjeA>?r6   __main__rb   )ru   
str | Nonerv   r   r   r:   r^   r`   )r^   r_   )%rf   
__future__r   r   r   osr   pathlibr   typingr   __file__resolveparent_SCRIPT_DIRra   pathinsertutils.loggerr   rc   r2   environr   r   r   _SESSION_MON_CFGrg   rh   ri   r-   r/   r0   rU   r'   r   r   rj   r6   r   <module>r      sh  " #   	 
   8n$$&--44{388#HHOOAs;'( #	H	**..!13HI 34 )--owG '++M2>D (,,^R@3F  1AQTX TXz #"/W"W"W" W" 
	W"~6 z	G r6   