
    #0j_#                        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
 ddlmZ ddlmZmZ  e
d          Z G d	 d
          Z G d d          ZdS )u  会话引擎池:每个活跃会话一个独立 WorldEngine + 线程。

生命周期(用户选定:离开即暂停续播)——
- 用户在看(有心跳):引擎在跑,token 在烧;
- 闲置超时(无心跳):存快照到 SQLite + 停引擎 + 移出内存,**不再烧 token**;
- 用户回来:从快照 restore 引擎、重启线程,接着原进度演。
并发上限:活跃引擎数封顶,超了就把最久没心跳的会话挤下去(存档暂停)。
成本隔离:每会话一个 MeteredLLM(底层 LiteLLM 客户端无状态、可共享)。
    )annotationsN)Path)
get_logger)WorldEngine)restore_enginesnapshot_enginezserver.poolc                  D    e Zd ZdZdddZddZddZddZddZddZ	dS )LiveSessionuR   一个用户的活世界:独立引擎线程 + 帧循环 + 叙事 + 自动存档。r   sidstrenginer   mgr'EngineManager'stepintc                   ||||f\  | _         | _        | _        | _        |                    |          |dd| _        t          j                    | _        t          j
                    | _        d| _        d| _        t          j        | j        dd|d d                    | _        | j                                         d S )NT)framer   runningFzsess-   targetdaemonname)r   r   llmr   r   state	threadingLocklocktime	monotonic	last_beat_restart_stopThread_runthreadstart)selfr   r   r   r   r   s         5/root/genesis-world/src/genesis/server/engine_pool.py__init__zLiveSession.__init__   s    47c4I1$+tx%||D114DQQ
N$$	))
&diK\SVWYXYWYSZK\K\]]]    returnNonec                    	 t          j        |                                            d S # t          $ r, t                              d| j        d d                    Y d S w xY w)Nu   [%s] 会话引擎线程崩溃r   )asynciorun_loop	Exceptionlogger	exceptionr   r(   s    r)   r%   zLiveSession._run'   so    	LK

%%%%% 	L 	L 	L<dhrrlKKKKKK	Ls   &* 2A A c           	       K   dd l }t          j                                        |j                            dd| j        d d                               | j        s| j        	                                 t          j
        |                                           }| j        j                            | j        j                   d {V  |                                 | j        r| j        syd| _        | j                            | j        d           | _        | j        5  d| j        d<   | j                            d          | j        d<   d d d            n# 1 swxY w Y   d S d S )	Nr   
   zdec-   )max_workersthread_name_prefixFr   r   )concurrent.futuresr/   get_running_loopset_default_executorfuturesThreadPoolExecutorr   r#   r   r'   create_task_framesschedr0   on_wakecancelr"   r   
new_enginer   r   r   r   )r(   
concurrentftasks      r)   r1   zLiveSession._loop-   s     !!!! ""7711bUj\`\degfgeg\hUjUj1kk	m 	m 	m* 	K'77E+#''(;<<<<<<<<<LLNNN} TZ  %"h11$(DAAY ? ?)*DJv&*.+*;*;A*>*>DJw'? ? ? ? ? ? ? ? ? ? ? ? ? ? ? E	 	s   "-EE"Ec                   K   d}| j         st          j        d           d {V  | j        d         s0| j        j        j        s| j        j                                         _| j        j        j        r*| j        j        | j        j        	                                 | j        
                                 | j        j                            | j        j        j                  rtd_        | j        j        j        | j        j                                        | j                                        cfd}t          j         |                       | j        5  | j        dxx         dz  cc<   | j                            | j        d                   | j        d<   d d d            n# 1 swxY w Y   t)          j                    |z
  d	k    r't)          j                    }|                                  | j         d S d S )
Ng        g      ?r   Tc                 x   K   	 t          j        j                    d {V  d_        d S # d_        w xY w)NF)r/   	to_threadwritebusy)labelnarnow_spanos   r)   _writez#LiveSession._frames.<locals>._writeP   sU      )%/	5%NNNNNNNNN#(5((((s   "0 	9r      r   <   )r#   r/   sleepr   r   clock_pausedpauseendingresumemaybe_fast_forwardnarratorduesim_secondsrL   rM   panoramar@   r   r   r   r    _persist)r(   	last_saverQ   rM   rN   rO   rP   s      @@@@r)   rA   zLiveSession._frames@   sd     	* 	 -$$$$$$$$$:i( {(0 .K%++---{ ( +T[-?-G!((***K**,,,+&C3774;+<+H#I#I%)[%6%BDKDUD[D[D]D]_c_j_s_s_u_u"ud) ) ) ) ) ) ) )
 #FFHH--- L L
6"""a'"""&*k&7&7
68J&K&K
7#L L L L L L L L L L L L L L L ~)+b00 N,,	3 * 	  	  	  	  	 s   )AF88F<?F<c                ,   	 t          j        t          | j                  d          }| j        j                            | j        | j        d         |           d S # t          $ r, t                              d| j        d d                    Y d S w xY w)NF)ensure_asciir   u   [%s] 存档失败r   )jsondumpsr   r   r   storesave_snapshotr   r   r2   r3   r4   )r(   snaps     r)   r_   zLiveSession._persist]   s    	@:odk::OOODHN((4:f3EtLLLLL 	@ 	@ 	@0$(2A2,??????	@s   AA 2BBc                x    |                                   d| _        | j        j                                         d S )NT)r_   r#   r   rB   stopr5   s    r)   stop_and_savezLiveSession.stop_and_saved   s3    
     r+   N)r   )r   r   r   r   r   r   r   r   r,   r-   )
__name__
__module____qualname____doc__r*   r%   r1   rA   r_   rj    r+   r)   r
   r
      s        \\    L L L L   &       :@ @ @ @! ! ! ! ! !r+   r
   c                  v    e Zd Zdddddd!dZd Zd Zd"dZd#dZd#dZd$dZ	d#dZ
d%dZd&dZd'dZd'dZd S )(EngineManager   g     V@r   g      >@)max_enginesidle_timeoutbrainratert   r   ru   floatrv   r   rw   c               >   t          |          | _        || _        || _        ||c| _        | _        ||c| _        | _        ||	c| _        | _	        i | _
        t          j                    | _        t          j        | j        dd                                           d S )NTreaperr   )r   	world_dirre   
shared_llmvizstagert   ru   rv   rw   _liver   RLock_lockr$   _reaperr'   )
r(   r{   re   shared_llm_clientr}   r~   rt   ru   rv   rw   s
             r)   r*   zEngineManager.__init__k   s     i
+"E$*.9<+$+ %t
DI-/
_&&
TIIIOOQQQQQr+   c                H    | j         dk    rd S ddlm}  || j                  S )Nstubr   )
MeteredLLM)rv   genesis.runtime.meteringr   r|   )r(   r   s     r)   	_make_llmzEngineManager._make_llmx   s8    :4777777z$/***r+   c           	        t          | j        | j        | j        ||d u           }|ddlm} ddlm} t          |di           pi } ||t          |j
                                                  |                    dd          |                    dd	          |                    d
                    |_        |j        j                            |j        j                    ||j                  |_        |t)          ||           |S )N)rw   rv   r   prologuer   )LongTerm)Narrator_lorenovel_titleu   《雾鸦》ghostu   阮青narrator_opening)rosterr   r   opening)r   r{   rw   rv   genesis.runtime.longtermr   genesis.runtime.narratorr   getattrlistmindskeysgetr[   bustapsappendtapgame_idlongtermr   )r(   r   	snap_dictengr   r   nlores          r)   rE   zEngineManager.new_engine~   s   $.ty
PS#,#46 6 6?999999999999C"--3E#8CSY^^5E5E0F0F05		-0X0X*/))GX*F*F,1II6H,I,IK K KCL GL 0111#8CK00CL 3	***
r+   r   r,   r
   c                8   | j         5  | j                            |          }|-|j        s&t	          j                    |_        |cddd           S t          | j                  | j        k    rkt          | j        
                                d           }t                              d|j        dd                    |                     |j                   | j                            |          \  }}|rt#          j        |          nd}|                                 }|                     ||          }t+          |||| |pd          }|| j        |<   t                              d|dd         |rd	nd
           |cddd           S # 1 swxY w Y   dS )uJ   确保会话引擎在跑:内存有则续,无则从快照恢复或新建。Nc                    | j         S N)r!   )xs    r)   <lambda>z&EngineManager.ensure.<locals>.<lambda>   s     r+   )keyu"   引擎池满,挤出闲置会话 %sr   r   )r   u   [%s] 会话引擎%su   恢复续播u   全新开局)r   r   r   r#   r   r    r!   lenrt   minvaluesr3   infor   _evictre   load_snapshotrc   loadsr   rE   r
   )	r(   r   lsoldestr   rg   r   r   r   s	            r)   ensurezEngineManager.ensure   s   Z 	 	$$B~bh~#~//		 	 	 	 	 	 	 	
 4:$"222TZ..006K6KLLL@&*RaR.QQQFJ'''11#66JD$,0:
4(((dI..""C//#y11CS#sDtyqAAAB DJsOKK-s2A2w)8gYghhh!	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	s   =FD0FFFr-   c                l    | j                             |d           }||                                 d S d S r   )r   poprj   r(   r   r   s      r)   r   zEngineManager._evict   s;    Z^^C&&> >r+   c                p    | j         5  |                     |           ddd           dS # 1 swxY w Y   dS )ul   主动暂停(关浏览器/切走时调用):立即存档停引擎,不再烧 token;回来要确认才恢复。N)r   r   r(   r   s     r)   suspendzEngineManager.suspend   s    Z 	 	KK	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	s   +//boolc                6    | j                             |          S )uB   会话存在(DB 有记录),不管引擎当前是否在内存跑。)re   session_existsr   s     r)   has_sessionzEngineManager.has_session   s    z((---r+   c                    | j                             |          }|t          j                    |_        | j                            |           d S r   )r   r   r   r    r!   re   touchr   s      r)   	heartbeatzEngineManager.heartbeat   sE    Z^^C  >>++BL
r+   LiveSession | Nonec                P    | j                             |          }|	|j        s|nd S r   )r   r   r#   r   s      r)   r   zEngineManager.get   s)    Z^^C  nRXnrrD@r+   dictc                   t          d | j                                        D                       }t          d | j                                        D                       }t          | j                  |t	          |d          dS )u<   所有活跃会话的 token 成本汇总(运维观测用)。c              3  ~   K   | ]8}|j         	|j                                                             d d          V  9dS )callsr   Nr   statsr   .0r   s     r)   	<genexpr>z+EngineManager.cost_total.<locals>.<genexpr>   sE      [[rTVTZ[BFLLNN&&w22[[[[[[r+   c              3  ~   K   | ]8}|j         	|j                                                             d d          V  9dS )est_cost_yuanr   Nr   r   s     r)   r   z+EngineManager.cost_total.<locals>.<genexpr>   sE      bbb[][ab26<<>>%%oq99bbbbbbr+   r8   )active_sessionsr   r   )sumr   r   r   round)r(   r   yuans      r)   
cost_totalzEngineManager.cost_total   s{    [[TZ=N=N=P=P[[[[[bbDJDUDUDWDWbbbbb#&tz??UUZ[_abUcUcdddr+   c                ^    	 t          j        d           t          j                     j        5   fd j                                        D             }|D ]:}t                              d|d d                                         |           ;	 d d d            n# 1 swxY w Y   )NTr7   c                @    g | ]\  }}|j         z
  j        k    |S rp   )r!   ru   )r   sr   nowr(   s      r)   
<listcomp>z)EngineManager._reaper.<locals>.<listcomp>   s2    eeeuq"cBL>PSWSd>d>d>d>d>dr+   u3   [%s] 闲置超时 → 存档暂停(停止烧 token)r   )	r   rT   r    r   r   itemsr3   r   r   )r(   staler   r   s   `  @r)   r   zEngineManager._reaper   s    	#JrNNN.""C # #eeeee
(8(8(:(:eee # #AKK UWXY[Z[Y[W\]]]KKNNNN## # # # # # # # # # # # # # #	#s   A$B##B'*B'c                    | j         5  t          | j                  D ]}|                     |           	 d d d            d S # 1 swxY w Y   d S r   )r   r   r   r   )r(   r   s     r)   shutdownzEngineManager.shutdown   s    Z 	 	$*%%  A	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	s   -AA
AN)rt   r   ru   rx   rv   r   rw   rx   )r   r   r,   r
   )r   r   r,   r-   )r   r   r,   r   )r   r   r,   r   )r,   r   rk   )rl   rm   rn   r*   r   rE   r   r   r   r   r   r   r   r   r   rp   r+   r)   rr   rr   j   s       $%T#4R R R R R R+ + +  "   (   
   
. . . .   A A A Ae e e e# # # #     r+   rr   )ro   
__future__r   r/   rc   r   r   pathlibr   genesis.obs.logging_setupr   genesis.runtime.enginer   genesis.runtime.persistencer   r   r3   r
   rr   rp   r+   r)   <module>r      s    # " " " " "              0 0 0 0 0 0 . . . . . . G G G G G G G G	M	"	"M! M! M! M! M! M! M! M!`d d d d d d d d d dr+   