
    Ji                        d Z ddlmZ ddlZddlmZmZ ddlmZm	Z	  ej                  e      ZdZe G d d             Z G d	 d
      Zy)u   publishing/consensus_pipeline.py — 합의도출 전용 파이프라인.

3대 엔진 합의도출 워크플로우를 실행한다.
MAX_CONSENSUS_ROUNDS = 3 (하드캡).

출처: memory/specs/three-engine-consensus.md
    )annotationsN)	dataclassfield)EngineResult
EngineRole   c                  t    e Zd ZU dZdZded<   dZded<   dZd	ed
<    ee	      Z
ded<    ee	      Zded<   y)ConsensusStateu   합의 상태 추적.r   intround_numberFbool	converged        floatconsensus_score)default_factoryz	list[str]minority_opinionszlist[list[EngineResult]]results_by_roundN)__name__
__module____qualname____doc__r   __annotations__r   r   r   listr   r        L/home/jay/workspace/services/multimodel-bot/publishing/consensus_pipeline.pyr
   r
      sD    L#It OU #(#>y>16t1L.Lr   r
   c                  L    e Zd ZdZd	d
dZedd       ZddZddZddZ	ddZ
y)ConsensusPipelineum   합의도출 파이프라인.

    Step 3 → Step 4 반복을 최대 MAX_CONSENSUS_ROUNDS까지 수행.
    c                0    || _         t               | _        y N)
_thresholdr
   _state)self	thresholds     r   __init__zConsensusPipeline.__init__&   s    #$&r   c                    | j                   S r!   )r#   r$   s    r   statezConsensusPipeline.state*   s    {{r   c                P   |syd}|D ]j  }|j                   r|j                  j                         t        fddD              }t        fddD              }||z   dk(  r|dz  }`||||z   z  z  }l |D cg c]  }|j                   r| }}|sy|t	        |      z  S c c}w )u   합의 점수 계산 (0.0 ~ 1.0).

        피드백의 "주요 수정 필요" 항목 수 기반.
        수정 필요 키워드가 적을수록 합의 점수가 높다.
        r   c              3  *   K   | ]
  }|v rd   yw   Nr   .0kwcontents     r   	<genexpr>z7ConsensusPipeline.evaluate_consensus.<locals>.<genexpr>=   "      # = #   )	u   수정 불필요u   충분u   적절u   양호u   우수zno major
acceptablez	well donegoodc              3  *   K   | ]
  }|v rd   ywr,   r   r.   s     r   r2   z7ConsensusPipeline.evaluate_consensus.<locals>.<genexpr>M   r3   r4   )	u   수정 필요u   부족u   오류u   잘못u   누락zmajor issue	incorrectmissingzneeds revisionr   g      ?)errorr1   lowersumlen)	r$   feedback_resultstotal_scoreresultpositive_signalsnegative_signalsr	non_errorr1   s	           @r   evaluate_consensusz$ConsensusPipeline.evaluate_consensus.   s      & (	XF||nn**,G" #
#     # #
#     "22a7s"/3CFV3VWWQ(	XT !1@1Q@	@S^++	 As   :B#B#c                t   | j                   xj                  dz  c_        | j                  |      }|| j                   _        | j                   j                  j                  |       | j                  |       || j                  k\  r3d| j                   _        t        j                  d|| j                         y| j                   j                  t        k\  r,t        j                  dt               d| j                   _        yt        j                  d|| j                  | j                   j                         y)u   추가 라운드가 필요한지 판단.

        Returns:
            True면 추가 라운드 필요 (합의 미달).
        r-   Tz/Consensus reached: score=%.2f >= threshold=%.2fFz,Max rounds reached (%d), forcing convergencez<Consensus not reached: score=%.2f < threshold=%.2f, round=%d)r#   r   rE   r   r   append_preserve_minorityr"   r   loggerinfoMAX_CONSENSUS_ROUNDSwarning)r$   r>   scores      r   should_continuez!ConsensusPipeline.should_continueh   s     	  A% ''(89&+#$$++,<= 	 01DOO#$(DKK!KKA
 ;;##';;NN>$ %*DKK!JOOKK$$		
 r   c                >   |D ]  }|j                   r|j                  j                         t        fddD              s?| j                  j
                  j                  d| j                  j                   d|j                   d|j                  dd          y)u   소수 의견 보존.c              3  &   K   | ]  }|v  
 y wr!   r   r.   s     r   r2   z7ConsensusPipeline._preserve_minority.<locals>.<genexpr>   s     vR2=vs   )u   반대u   동의하지 않u   우려concerndisagreehoweverz[Round z][z] N   )	r:   r1   r;   anyr#   r   rG   r   engine)r$   resultsr@   r1   s      @r   rH   z$ConsensusPipeline._preserve_minority   s     	F||nn**,Gv+uvv--44dkk667r&--6>>Z^[^K_J`a	r   c                "    t               | _        y)u   상태 초기화.N)r
   r#   r(   s    r   resetzConsensusPipeline.reset   s    $&r   N)g      ?)r%   r   returnNone)rZ   r
   )r>   list[EngineResult]rZ   r   )r>   r\   rZ   r   )rW   r\   rZ   r[   )rZ   r[   )r   r   r   r   r&   propertyr)   rE   rN   rH   rY   r   r   r   r   r       s6    
'  8,t%N
'r   r   )r   
__future__r   loggingdataclassesr   r   engine_v2.engine_resultr   r   	getLoggerr   rI   rK   r
   r   r   r   r   <module>rc      sX    #  ( <			8	$  M M M}' }'r   