
    Ri2=                        d Z ddlmZ ddlZddlZddlmZmZ ddlm	Z	 g dZ
ddZddZ G d	 d
ee	      Ze G d d             Z G d d      Zy)u   토론 매니저: 봇 간 라운드 로빈 토론 모드를 관리합니다.

여러 봇(잼민이/코덱스/클로디)이 순서를 정해 토론할 수 있도록
턴 관리, 시간 제한, idle 타임아웃을 처리합니다.
    )annotationsN)	dataclassfield)Enum)u   코드u   파일u   소스u   함수u	   클래스u   컴포넌트u   구현u   로직u   아키텍처u   구조u   읽어u	   분석해u   소스코드u   디렉토리u   모듈u   라이브러리u	   패키지importu   설정파일configcodefilesourcefunctionclassarchitecture	structureanalyzeimplementation	directoryc                    | j                         t        fdt        D              }t        t	        j
                  d            }|dk\  xs |S )u=   유저 메시지에서 코드 분석 필요 여부를 감지.c              3  ,   K   | ]  }|v sd   yw)   N ).0kw
text_lowers     [/home/jay/workspace/.worktrees/task-2117-dev1/services/multimodel-bot/discussion_manager.py	<genexpr>z'detect_code_analysis.<locals>.<genexpr>7   s     ObbJ>NOs   	z)(/home/|\.py|\.js|\.ts|\.tsx|\.json|\.md)   )lowersumCODE_ANALYSIS_KEYWORDSboolresearch)message_textkeyword_counthas_pathr   s      @r   detect_code_analysisr&   4   sH    ##%JO$:OOMBIIJJWXHA))    c                   d}t        j                  d|       }|r+t        |j                  d            }|dz  }t	        ||      S t        j                  d|       }|r&t        |j                  d            }t	        ||      S t        j                  d| t         j
                        }|r+t        |j                  d            }|dz  }t	        ||      S t        j                  d| t         j
                        }|r+t        |j                  d            }|dz  }t	        ||      S t        j                  d| t         j
                        }|r&t        |j                  d            }t	        ||      S y	)
u   유저 메시지에서 시간 키워드를 파싱하여 분 단위로 반환.

    지원 패턴:
    - "N분간", "N분동안", "N분"
    - "N시간동안", "N시간"
    - "Nmin", "Nhour", "Nhr"

    최대 180분(3시간). 없으면 None 반환.
       u   (\d+)\s*시간r   <   u   (\d+)\s*분z(\d+)\s*hours?z
(\d+)\s*hrz(\d+)\s*minN)r!   r"   intgroupmin
IGNORECASE)textMAX_MINUTESmatchhoursminutess        r   parse_durationr4   A   s?    K II'.EEKKN#"*7K(( IInd+Eekk!n%7K(( II'r}}=EEKKN#"*7K(( IImT2==9EEKKN#"*7K(( IIndBMM:Eekk!n%7K((r'   c                      e Zd ZdZdZdZy)DiscussionPhasedivergeconverge	consensusN)__name__
__module____qualname__DIVERGECONVERGE	CONSENSUSr   r'   r   r6   r6   v   s    GHIr'   r6   c                      e Zd ZU dZdZded<   dZded<   dZd	ed
<   dZded<   dZ	ded<   dZ
ded<   dZded<   dZded<   dZded<   dZded<   dZded<   y)DiscussionStateu   chat_id별 토론 상태.Fr    active        float
start_timeN
int | Noneduration_minuteslast_user_message_time strcurrent_turnr   r+   chat_idcode_analysis_moderound_countbot_response_countgpt-5.1-codex-minicodex_modeldeep_announced)r:   r;   r<   __doc__rB   __annotations__rE   rG   rH   rK   rL   rM   rN   rO   rQ   rR   r   r'   r   rA   rA      sw    #FDJ#'j'$'E'L#GS$$K+K+ ND r'   rA   c                      e Zd ZU dZg dZded<   dZded<   dZded	<   d
Zded<   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%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y)-DiscussionManageru   봇 간 라운드 로빈 토론 모드를 관리하는 클래스.

    - 유저 메시지 시 토론 모드 진입
    - 봇 응답 후 라운드 로빈으로 다음 턴 설정
    - 5분 idle 또는 지정 시간 초과 시 토론 종료
    )gemini_view_botcodex_view_botclaude_view_botz	list[str]BOT_USERNAMESi  r+   IDLE_TIMEOUTr)   MAX_DURATION   
TURN_DELAYc                <    i | _         d| _        d| _        i | _        y )Nr   )_states_active_chat_id_start_index_chain_runningselfs    r   __init__zDiscussionManager.__init__   s!    35$%!"/1r'   c                n    || j                   vrt        |      | j                   |<   | j                   |   S )uB   chat_id에 해당하는 상태를 반환. 없으면 신규 생성.)rL   )r`   rA   re   rL   s     r   
_get_statezDiscussionManager._get_state   s0    $,,&$3G$DDLL!||G$$r'   c                l    | j                   dk(  ry| j                  j                  | j                         S )u9   현재 활성 채팅의 상태를 반환. 없으면 None.r   N)ra   r`   getrd   s    r   _active_statezDiscussionManager._active_state   s.    1$|| 4 455r'   c                :    | j                   j                  |d      S )uA   해당 채팅에서 봇 응답 체인이 실행 중인지 반환.F)rc   rk   rh   s     r   is_chain_runningz"DiscussionManager.is_chain_running   s    ""&&w66r'   c                "    || j                   |<   y)u*   봇 응답 체인 실행 상태를 설정.N)rc   )re   rL   runnings      r   set_chain_runningz#DiscussionManager.set_chain_running   s    '.G$r'   c                V    | j                   j                  |      }|y|j                  S )uF   해당 채팅에서 토론 모드가 활성화되어 있는지 반환.F)r`   rk   rB   re   rL   states      r   is_discussion_activez&DiscussionManager.is_discussion_active   s(      )=||r'   c                X    | j                   j                  |      }|r|j                  S dS )u3   해당 채팅이 코드 분석 모드인지 반환.F)r`   rk   rM   rs   s      r   is_code_analysis_modez'DiscussionManager.is_code_analysis_mode   s)      )+0u'';e;r'   c                X    | j                   j                  |      }|r|j                  S dS )u,   해당 채팅의 코덱스 모델을 반환.rP   )r`   rk   rQ   rs   s      r   get_codex_modelz!DiscussionManager.get_codex_model   s*      )$)u  C/CCr'   c                    | j                   j                  |      }|r#|j                  dk(  r|j                  sd|_        yy)u9   deep 모드 안내가 필요하면 True 반환 (1회만).gpt-5.2-codexTF)r`   rk   rQ   rR   rs   s      r   should_announce_deep_modez+DiscussionManager.should_announce_deep_mode   s<      )U&&/9%BVBV#'E r'   c                    | j                   j                  |      }||j                  sy|j                  j	                         |j	                         k(  S )u7   해당 채팅에서 현재 이 봇의 턴인지 확인.F)r`   rk   rB   rK   r   )re   bot_usernamerL   rt   s       r   
is_my_turnzDiscussionManager.is_my_turn   sG      )=!!'')\-?-?-AAAr'   c                &   | j                  |      }t        j                         }|}d|v r |j                  dd      j                         }t	        |      }d}|j
                  sd}d|_        ||_        ||_        d|v rd|_        nd|_        t        |      rd|_
        d|_        n;d|_
        | j                  | j                  t        | j                        z     |_        | xj                  dz  c_        || _        n	|||_        ||_        |S )	u   유저 메시지 수신 시 호출. 새 토론 시작이면 True, 기존 토론 중이면 False 반환.

        - 시간 키워드 파싱
        - last_user_message_time 갱신
        - 토론 모드 활성화
        z--deeprI   FTr{   rP   rY   r   )ri   timereplacestripr4   rB   rE   rG   rQ   r&   rM   rK   rZ   rb   lenra   rH   )re   rL   r#   rt   nowcleaned_textdurationis_news           r   on_user_messagez!DiscussionManager.on_user_message   s    (iik $|#'//"=CCEL!,/||FEL"E%-E"<'$3!$8!#L1+/(%6"+0(%)%7%78I8ICPTPbPbLc8c%d""#*D  #)1&'*$r'   c                4   | j                         }||j                  sy|xj                  dz  c_        |j                  dk\  r|xj                  dz  c_        d|_        | j	                         r| j                          y| j                         }||_        |S )u   봇이 응답한 후 호출.

        라운드 로빈으로 다음 턴을 설정하고 다음 봇 username을 반환.
        토론이 종료되어야 하면 None 반환.
        Nr      r   )rl   rB   rO   rN   check_should_stopstop_discussionget_next_botrK   )re   r~   rt   next_bots       r   on_bot_responsez!DiscussionManager.on_bot_response  s     ""$= 	  A% ##q("'(E$!!#  "$$&%r'   c                    | j                         }||j                  sy| j                         r| j                          y| j	                         }||_        |S )u   다음 봇으로 턴 진행.

        시간 초과/idle 체크 후:
        - 계속: 다음 봇 username 반환
        - 종료: None 반환
        N)rl   rB   r   r   r   rK   )re   rt   r   s      r   advance_turnzDiscussionManager.advance_turn-  sV     ""$=!!#  "$$&%r'   c                    | j                         }|_d|_        d|_        d|_        d|_        d|_        d|_        d|_        d|_        d|_	        d|_
        d| j                  |j                  <   d| _        y)u1   토론 중단, 활성 chat_id의 state 초기화.NFrI   rC   r   rP   )rl   rB   rK   rG   rE   rH   rM   rN   rO   rQ   rR   rc   rL   ra   )re   rt   s     r   r   z!DiscussionManager.stop_discussion@  s    ""$ EL!#E%)E""E+.E(',E$ !E'(E$ 4E#(E 16D. r'   c                <   | j                         }||j                  s| j                  d   S |j                  }	 | j                  j                  |      }|dz   t        | j                        z  }| j                  |   S # t        $ r | j                  d   cY S w xY w)u2   라운드 로빈으로 다음 봇 username 반환.r   r   )rl   rK   rZ   index
ValueErrorr   )re   rt   currentidxnext_idxs        r   r   zDiscussionManager.get_next_botQ  s    ""$= 2 2%%a(($$	)$$**73C !Gs4#5#566!!(++	  	)%%a((	)s   B   BBc                0   | j                   j                  |      }||j                  st        j                  S |j
                  t        j                         |j                  z
  }|j
                  dz  }|dk  rt        j                  S ||z  }|dk  rt        j                  S |dk  rt        j                  S t        j                  S |j                  dk  rt        j                  S |j                  dk  rt        j                  S t        j                  S )uT  현재 토론 Phase를 계산.

        시간 지정 모드 (duration_minutes != None):
          - 0~60%: DIVERGE
          - 60~90%: CONVERGE
          - 90~100%: CONSENSUS

        idle 모드 (duration_minutes == None):
          - 라운드 1~3: DIVERGE
          - 라운드 4~6: CONVERGE
          - 라운드 7+: CONSENSUS
        r*   r   g333333?g?r      )r`   rk   rB   r6   r=   rG   r   rE   r>   r?   rN   )re   rL   rt   elapsed_secondstotal_secondsratios         r   get_current_phasez#DiscussionManager.get_current_phase`  s       )="***!!-"iikE,<,<<O!22R7M!&...#m3Es{&...&///&000   1$&...""Q&&///&000r'   c                    | j                         }||j                  syt        j                         }|j                  !||j                  z
  dz  }||j                  k\  S ||j
                  z
  }|| j                  k\  S )u  토론을 종료해야 하는지 확인.

        - 시간 한정 모드(duration_minutes 지정): 경과 시간이 duration_minutes 초과 시 True
        - idle 모드(duration_minutes=None): last_user_message_time으로부터 5분 경과 시 True
        Tr*   )rl   rB   r   rG   rE   rH   r[   )re   rt   r   elapsed_minutesidle_secondss        r   r   z#DiscussionManager.check_should_stop  s     ""$=iik!!-"U%5%55;O"e&<&<<< !=!==L4#4#444r'   c                    | j                  |      s|ryy| j                         r| j                          y| j                  |      }|j                  j                         |j                         k(  S )u8  이 봇이 해당 메시지에 토론 컨텍스트에서 응답해야 하는지 판단.

        - 토론 모드가 아닐 때: 봇 메시지에 응답 안함 (기존 동작 유지)
        - 토론 모드일 때: current_turn이 자신이면 True, 아니면 False
        - 시간/idle 체크도 수행
        F)ru   r   r   ri   rK   r   )re   r~   sender_usernamesender_is_botrL   rt   s         r   should_bot_respondz$DiscussionManager.should_bot_respond  si     ((1 !!#  "(!!'')\-?-?-AAAr'   N)returnNone)rL   r+   r   rA   )r   zDiscussionState | None)rL   r+   r   r    )rL   r+   rp   r    r   r   )rL   r+   r   rJ   )r~   rJ   rL   r+   r   r    )rL   r+   r#   rJ   r   r    )r~   rJ   r   
str | None)r   r   )r   rJ   )rL   r+   r   r6   )r   r    )
r~   rJ   r   rJ   r   r    rL   r+   r   r    )r:   r;   r<   rS   rZ   rT   r[   r\   r^   rf   ri   rl   rn   rq   ru   rw   ry   r|   r   r   r   r   r   r   r   r   r   r   r'   r   rV   rV      s      XM9WL#L#J2%67/<
D
B+Z0&!",(1T5*BB B 	B
 B 
Br'   rV   )r#   rJ   r   r    )r/   rJ   r   rF   )rS   
__future__r   r!   r   dataclassesr   r   enumr   r   r&   r4   rJ   r6   rA   rV   r   r'   r   <module>r      sd    # 	  (  D*-jc4  ! ! !*bB bBr'   