
    +jn                    t    d Z ddlmZ ddlZddlZddlmZ ddlmZm	Z	 dZ
dZdZd	ZddZ G d d          ZdS )u  MeteredLLM:LLM 成本仪表,包装任意带 .complete(messages, **kw) 的客户端。

- 每次调用累计次数与估算费用(中文粗估:字符数 // 2 ≈ token 数);
- 价格按 DeepSeek flash 量级(输入 ¥0.001/千token、输出 ¥0.002/千token),常量可调;
- 支持 _tag 分层(L1/L2/narrator)累计,_tag 从 kwargs 弹出、不传给底层;
- stats() 提供面板所需汇总,含最近一分钟调用数(滑动窗口,烧钱告警用);
- threading.Lock 保证多线程调用安全。
    )annotationsN)deque)AnyCallablegMbP?gMb`?L2g      N@textstrreturnintc                &    t          |           dz  S )u(   中文粗估:约 2 字符 ≈ 1 token。   )len)r   s    3/root/genesis-world/src/genesis/runtime/metering.py_est_tokensr      s    t99>    c                  @    e Zd ZdZdddd
ZddZddZddZddZdS )
MeteredLLMuT   透明包装 LLM 客户端:转发 complete,同时计量调用数 / token / 费用。N)time_fninnerr   r   Callable[[], float] | Noner
   Nonec                   || _         |pt          j        | _        t	          j                    | _        d| _        d| _        i | _	        t                      | _        d S )Nr           )_innertime	monotonic_time_fn	threadingLock_lock_calls
_cost_yuan_by_tagr   _recent)selfr   r   s      r   __init__zMeteredLLM.__init__#   sJ    14>^%%
46%*WWr   messages
list[dict]kwargsr	   c                |   t          |                    dt                              } | j        j        |fi |}t          d |D                       }t          |          }|t          z  |t          z  z   dz  }| 	                                }| j
        5  | xj        dz  c_        | xj        |z  c_        | j                            |ddd          }	|	dxx         dz  cc<   |	d	xx         |z  cc<   | j                            |           |                     |           d d d            n# 1 swxY w Y   |S )
N_tagc           	   3  v   K   | ]4}t          t          |                    d d                              V  5dS )content N)r   r	   get).0ms     r   	<genexpr>z&MeteredLLM.complete.<locals>.<genexpr>/   s@      QQ1Ci(<(<$=$=>>QQQQQQr   g     @@   r   r   callscostr5   r6   )r	   popDEFAULT_TAGr   completesumr   PRICE_IN_YUAN_PER_KTOKENPRICE_OUT_YUAN_PER_KTOKENr   r    r!   r"   r#   
setdefaultr$   append_trim)
r%   r'   r)   tagresult	tokens_in
tokens_outr6   nowslots
             r   r9   zMeteredLLM.complete,   s   &**V[1122%%h99&99QQQQQQQ	 ((
44889<BCmmooZ 	 	KK1KKOOt#OO<**3!S0I0IJJDMMMQMMMLLLD LLLL$$$JJsOOO	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 s   BD11D58D5dictc                :   |                                  }| j        5  |                     |           | j        t	          | j        d          d | j                                        D             t          | j	                  dcddd           S # 1 swxY w Y   dS )uA   面板汇总:{calls, est_cost_yuan, by_tag, calls_last_minute}。r   c                n    i | ]2\  }}|t          |d                    t          |d         d          d3S )r5   r6      r4   )r   round)r0   tss      r   
<dictcomp>z$MeteredLLM.stats.<locals>.<dictcomp>F   sO     = = =#q! AgJqyRSATATUU = = =r   )r5   est_cost_yuanby_tagcalls_last_minuteN)
r   r    r?   r!   rJ   r"   r#   itemsr   r$   r%   rD   s     r   statszMeteredLLM.stats>   s    mmooZ 	 	JJsOOO!&t!:!:= ='+|'9'9';';= = =%(%6%6 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	s   A'BBBrD   floatc                    | j         rT|| j         d         z
  t          k    r?| j                                          | j         r|| j         d         z
  t          k    9dS dS dS dS )u5   丢弃窗口外的时间戳(调用方需已持锁)。r   N)r$   WINDOW_SECONDSpopleftrR   s     r   r?   zMeteredLLM._trimK   sv    l 	#sT\!_4~EEL  """ l 	#sT\!_4~EEEE 	# 	# 	# 	#EEr   namec                ,    t          | j        |          S )uC   其余属性透传底层客户端,保证包装对调用方透明。)getattrr   )r%   rX   s     r   __getattr__zMeteredLLM.__getattr__P   s    t{D)))r   )r   r   r   r   r
   r   )r'   r(   r)   r   r
   r	   )r
   rF   )rD   rT   r
   r   )rX   r	   r
   r   )	__name__
__module____qualname____doc__r&   r9   rS   r?   r[    r   r   r   r       s        ^^LP - - - - - -   $   # # # #
* * * * * *r   r   )r   r	   r
   r   )r_   
__future__r   r   r   collectionsr   typingr   r   r;   r<   r8   rV   r   r   r`   r   r   <module>rd      s     # " " " " "                           
 ! !    
2* 2* 2* 2* 2* 2* 2* 2* 2* 2*r   