
    Qi                     L    d Z ddlZddlZddlmZ g dZdZdZdZ G d d	      Z	y)
u   token_ledger.py — 일일 토큰 사용량 및 파이프라인 동시 실행 한도 관리.

작성자 : 토르 (dev2-team backend)
날짜   : 2026-03-24
    N)Path)TokenLedgerTOKENS_PER_MINUTE_OPUSTOKENS_PER_MINUTE_SONNETCONSERVATIVE_MULTIPLIERi  i  g333333?c                       e Zd ZdZdZdZdZdeez  ddfdZ	de
fd	Zde
fd
ZddZdededdfdZdededefdZdefdZdededefdZde
fdZddedefdZy)r   i@B          ledger_pathreturnNc                 N    t        |      | _        | j                         | _        y )N)r   _path_load_state)selfr   s     J/home/jay/workspace/.worktrees/task-2117-dev1/orchestrator/token_ledger.py__init__zTokenLedger.__init__   s    +&
 JJL    c                 b    t         j                  j                         j                         di dS )Nr   )datedaily_total	pipelines)datetimer   today	isoformatr   s    r   _empty_statezTokenLedger._empty_state    s+    MM'')335
 	
r   c                 
   | j                   j                         s| j                         S 	 | j                   j                  d      }t	        j
                  |      }|j                  d      t        j                  j                         j                         k7  r| j                         S |j                  dd       |j                  di        |S # t        j                  t        t        f$ r | j                         cY S w xY w)Nutf-8encodingr   r   r   r   )r   existsr   	read_textjsonloadsgetr   r   r   r   
setdefaultJSONDecodeErrorOSErrorKeyError)r   rawdatas      r   r   zTokenLedger._load'   s    zz  "$$&&
	'**&&&8C::c?Dxx8==#6#6#8#B#B#DD((**OOM1-OOK,K$$gx8 	'$$&&	's   A>C +%C .DDc                     	 | j                   j                  j                  dd       | j                   j                  t	        j
                  | j                  dd      d       y # t        $ r Y y w xY w)NT)parentsexist_okFr
   )ensure_asciiindentr    r!   )r   parentmkdir
write_textr%   dumpsr   r*   r   s    r   _savezTokenLedger._save6   s`    	JJ##D4#@JJ!!$**T[[uUV"Wbi!j 		s   A$A' '	A32A3pipeline_idtokensc                     | j                   d   }|j                  |d      |z   ||<   | j                   j                  dd      |z   | j                   d<   | j                          y)uG   pipeline_id별 토큰 사용량을 누적하고 파일에 저장한다.r   r   r   N)r   r'   r7   )r   r8   r9   r   s       r   record_usagezTokenLedger.record_usageA   sU    ++k2	!*{A!>!G	+%)[[__]A%F%OM"

r   c                    | j                   j                  dd      }| j                   j                  di       }||v}||z   | j                  kD  ry|rt        |      | j                  k\  ry|rt        |      | j
                  k\  ryy)uC   지정한 토큰을 소비할 수 있는지 여부를 반환한다.r   r   r   FT)r   r'   DAILY_HARD_LIMITlenMAX_CONCURRENT_PIPELINESMAX_PIPELINE_STARTS_PER_DAY)r   r8   r9   r   r   is_news         r   	can_spendzTokenLedger.can_spendH   s    ;;??=!<++//+r:	I- $"7"77 c)n(E(EE c)n(H(HHr   c                 L    t        | j                  j                  dd            S )u6   현재 날짜의 총 토큰 사용량을 반환한다.r   r   )intr   r'   r   s    r   get_daily_usagezTokenLedger.get_daily_usage\   s    4;;??=!455r   team_idduration_secondsc                 d    |dk  ry|dz  }d|v rt         }nt        }t        ||z  t        z        S )u  task-timers.json 세션 시간 기반 토큰 추정.

        Args:
            team_id: 팀 식별자 (예: "dev1-team")
            duration_seconds: 세션 지속 시간(초)

        Returns:
            추정 토큰 수 (정수)

        로직:
        - duration_seconds <= 0이면 0 반환
        - 기본 rate: TOKENS_PER_MINUTE_SONNET (3000 tokens/min)
        - 보수적 계수: CONSERVATIVE_MULTIPLIER (1.2)
        - 계산: int(duration_minutes * rate * CONSERVATIVE_MULTIPLIER)

        Note: 현재는 모든 세션을 Sonnet rate로 추정 (Opus 세션 구분이 불확실하므로 보수적).
              향후 team_id에서 팀장/팀원 구분 가능 시 rate 분기.
        r   g      N@u   팀장)r   r   rD   r   )r   rF   rG   duration_minutesrates        r   estimate_session_tokensz#TokenLedger.estimate_session_tokens`   sC    & q +d2w)D+D#d*-DDEEr   c                 v    | j                         }| j                  }||z
  }t        ||z  dz  d      }||||dS )ug  당일 토큰 사용량 + 잔여 예산 반환.

        Returns:
            {
                "today": int,           # 오늘 사용량
                "limit": int,           # DAILY_HARD_LIMIT
                "remaining": int,       # limit - today
                "percentage": float,    # (today / limit) * 100, 소수점 1자리
            }
        d      )r   limit	remaining
percentage)rE   r=   round)r   r   rO   rP   rQ   s        r   get_daily_usage_summaryz#TokenLedger.get_daily_usage_summary}   sQ     $$&%%EM	EEMS0!4
"$	
 	
r   	thresholdc                 F    | j                         }||| j                  z  k\  S )u   사용량이 DAILY_HARD_LIMIT의 threshold 비율 이상이면 True.

        Args:
            threshold: 0.0 ~ 1.0 사이 비율 (기본 0.8 = 80%)

        Returns:
            daily_total >= threshold * DAILY_HARD_LIMIT이면 True
        )rE   r=   )r   rT   r   s      r   check_warning_thresholdz#TokenLedger.check_warning_threshold   s(     **,i$*?*????r   )r   N)g?)__name__
__module____qualname__r=   r?   MAX_RETRIES_PER_STEPr@   r   strr   dictr   r   r7   rD   r;   boolrB   rE   floatrK   rS   rV    r   r   r   r      s      "$)D3J )4 )
d 
't ' S T S # $ (6 6Fs Fe FPS F:
 
,
@ 
@ 
@r   r   )
__doc__r   r%   pathlibr   __all__r   r   r   r   r_   r   r   <module>rc      s:      
j   K@ K@r   