
    Jis                        d Z ddlm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 ddlmZmZmZ dd	lmZmZ  ej*                  e      Zed
   ZdZ G d d      Zy)uH   engine_v2/engine_orchestrator.py — 멀티엔진 오케스트레이터.    )annotationsN)Literal)cost_tracker)CircuitBreaker)	CLIRunner)check_error_gate
check_gatesanitize)EngineResult
EngineRole)
SEQUENTIALPARALLEL	BROADCAST   c                      e Zd ZdZd
dZ	 d	 	 	 	 	 	 	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 	 	 	 	 ddZ		 	 	 	 	 	 	 	 	 	 	 	 ddZ
y	)EngineOrchestratoru   멀티엔진 오케스트레이터.

    모드:
        SEQUENTIAL — 직렬 실행 (Semaphore 불필요)
        PARALLEL — asyncio.gather + Semaphore(3)
        BROADCAST — 동일 프롬프트를 모든 엔진에 병렬 전송
    c                    t        j                  t              | _        t	               t	               t	               d| _        y )N)claudegeminicodex)asyncio	Semaphore_SEMAPHORE_LIMIT
_semaphorer   	_breakers)selfs    L/home/jay/workspace/services/multimodel-bot/engine_v2/engine_orchestrator.py__init__zEngineOrchestrator.__init__    s0    !++,<=$&$&#%5
    c                   K   |dk(  r| j                  |||||       d{   S |dk(  r| j                  |||||       d{   S |dk(  r| j                  |||||       d{   S t        d|       7 V7 67 w)u   엔진 실행.r   Nr   r   zUnknown mode: )_run_sequential_run_parallel_run_broadcast
ValueError)r   modepromptsenginestask_idsteptimeouts          r   runzEngineOrchestrator.run(   s      <--gwwWWWZ++GWgtWUUU[ ,,WgwgVVV~dV455 XUVs3   A=A7!A=A9!A=$A;%A=9A=;A=c                  K   g }t        t        ||            D ]  \  }\  }}	| j                  |	||||z   |       d{   }
t        |
j                        r;t
        j                  d|
j                         d|
_        |j                  |
        |S t        |
j                  |
j                        r)t
        j                  d       |j                  |
        |S |j                  |
        |S 7 w)u   직렬 실행.Nz#L3 gate triggered: flagged_count=%dTz)L4 gate triggered: error without fallback)	enumeratezip_call_enginer	   flagged_countloggerwarningerrorappendr   fallback_used)r   r&   r'   r(   r)   r*   resultsipromptengineresults              r   r!   z"EngineOrchestrator._run_sequential;   s      ')#,S'-B#C 	#A,,VVWdQhPWXXF&../DfFZFZ[#v&   f.B.BCJKv& NN6"	#  Ys   <C3C1B3C3c                   K   d fd}t        t        t        ||                  }|D 	
cg c]  \  }\  }	}
 ||
|	||z          }}	}}
t        j                  |ddi d{   } j                  |||      S c c}
}	}w 7 w)u!   병렬 실행 (Semaphore 제한).c                   K   j                   4 d {    j                  | ||       d {   cd d d       d {    S 7 27 7 	# 1 d {  7  sw Y   y xY wwNr   r/   )r9   r8   sr   r(   r*   s      r   _taskz/EngineOrchestrator._run_parallel.<locals>._task]   sc      T T!..vvw7SST T TST T T TS   A"AA"AA	AA"AA"	AA"AAAA"return_exceptionsTN)r9   r   r8   strr?   intreturnr   )listr-   r.   r   gather_unwrap)r   r&   r'   r(   r)   r*   r@   pairsr7   pmtengcorosraws   `  ` `       r   r"   z EngineOrchestrator._run_parallelS   s     	T Ys7G456@EFF}q*3sC*FFNNEBTBB||C'488 GBs   -B
BB
(B) B
c                    	K   |sg S |d   	d	 fd}t        j                  |D cg c]
  } ||       c}ddi d{   } j                  ||      S c c}w 7 w)u6   동일 프롬프트를 모든 엔진에 병렬 전송.r   c                   K   j                   4 d {    j                  |        d {   cd d d       d {    S 7 27 7 	# 1 d {  7  sw Y   y xY wwr=   r>   )r9   r8   r   r)   r(   r*   s    r   r@   z0EngineOrchestrator._run_broadcast.<locals>._tasks   sc      W W!..vvwgVVW W WVW W W WrA   rB   TN)r9   r   rE   r   )r   rG   rH   )
r   r&   r'   r(   r)   r*   r@   erM   r8   s
   `  ```   @r   r#   z!EngineOrchestrator._run_broadcastf   sl      I	W 	W NNw$?!U1X$?XSWXX||C'488 %@Xs   'A'A 
A'A%A'c                    g }t        |      D ]M  \  }}t        |t              r'|j                  t	        ||   dd|||z   d             =|j                  |       O |S )u>   gather 결과에서 Exception을 error EngineResult로 변환. Tr9   contentcleanr(   r)   r3   )r-   
isinstanceBaseExceptionr4   r   )r   rM   r'   r(   r)   outr7   rs           r   rH   zEngineOrchestrator._unwrapz   si     #%cN 	DAq!]+

 
BbRY`dgh`hptu 

1	 
r   c                  K   | j                   j                  |      }|7|j                         s't        j	                  d|       t        |dd||d      S t        j                         }	 |dk(  r t        j                  ||       d{   }nX|dk(  r t        j                  ||       d{   }n3|d	k(  r t        j                  ||       d{   }nt        d
|       |j                  dk7  xs |j                  }	|	s|j                  n|j                   }
t#        |
      \  }}t        ||
|||t%        |
      dz  |	|j&                  |	      }t        j                         |z
  }|-|j*                  r|j-                          n|j/                          t1        j2                  |t%        |      |       |S 7 67 7 # t(        $ r t        |dd||d      }Y w xY ww)u?   단일 엔진 호출 + Sanitize + CircuitBreaker + CostTracker.Nz&CircuitBreaker OPEN: engine=%s blockedrR   TrS   r   )r*   r   r   zUnknown engine: r      )	r9   rT   rU   r(   r)   	token_estr3   r5   r0   )prompt_charsduration_sec)r   getallow_requestr1   r2   r   time	monotonicr   
run_claude
run_gemini	run_codexr$   
returncode	timed_outstdoutstderrr
   lenr5   	Exceptionr3   record_failurerecord_successr   	log_usage)r   r9   r8   r(   r)   r*   breakerstart
cli_resultis_errorrM   rU   flaggedr:   durations                  r   r/   zEngineOrchestrator._call_engine   s     ..$$V,w'<'<'>NNCVLvrW[_gkll 	o!#,#7#7#PP
8##,#7#7#PP
7"#,#6#6vw#OO
 #3F8!<==!,,1IZ5I5IH+3*##9J9JC%c]NE7!c(a-(66%
F >>#e+||&&(&&(vCKhWE QPO$  	o!2w]aimnF	osb   A)G),G	 G$G	 0G1$G	 GBG	 A)G)G	 G	 G	 	G&#G)%G&&G)N)rE   None)iX  )r%   Moder&   	list[str]r'   list[EngineRole]r(   rC   r)   rD   r*   rD   rE   list[EngineResult])r&   rw   r'   rx   r(   rC   r)   rD   r*   rD   rE   ry   )
rM   z"list[EngineResult | BaseException]r'   rx   r(   rC   r)   rD   rE   ry   )r9   r   r8   rC   r(   rC   r)   rD   r*   rD   rE   r   )__name__
__module____qualname____doc__r   r+   r!   r"   r#   rH   r/    r   r   r   r      s   
 66 6 "	6
 6 6 6 
6& " 	
   
099 "9 	9
 9 9 
9&99 "9 	9
 9 9 
9(/ " 	
  
$44 4 	4
 4 4 
4r   r   )r}   
__future__r   r   loggingra   typingr   	engine_v2r   engine_v2.circuit_breakerr   engine_v2.cli_runnerr   engine_v2.content_sanitizerr   r	   r
   engine_v2.engine_resultr   r   	getLoggerrz   r1   rv   r   r   r~   r   r   <module>r      sX    N "     " 4 * N N <			8	$45 i ir   