
    <j(<              	         U 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 ddl	m
Z
 ddlmZ  ee      Z eej                  j!                  dd	            Z	 dd
lmZ  ej*                         ZdZ	 ddlmZ  e       Zded<   dZd$dZd%dZ g dZ!ded<   h dZ"ded<   ddddddddd Z#ded!<    G d" d#      Z$y# e$ r dZdZY Uw xY w# e$ r i ZdZY Ow xY w)&uD  BotStatusManager — 봇 상태 관리 단일 소스 모듈.

dispatch.py, whisper-compile.py 에 분산되어 있던 봇 상태 관련 로직을 하나로 통합.

Usage:
    from utils.bot_status import BotStatusManager

    mgr = BotStatusManager()
    busy  = mgr.get_busy_bots()
    idle  = mgr.get_idle_bots()
    avail = mgr.is_bot_available("bot-e")
    status = mgr.get_team_status("dev4-team")
    occ   = mgr.get_bot_occupation()
    team  = mgr.suggest_team("배너 이미지 디자인 작업")
    warn  = mgr.validate_routing("dev1-team", "카피 마케팅 전략")
    )annotationsN)Path)Optional)COMPOSITE_TRIGGER_KEYWORDS)
get_loggerWORKSPACE_ROOTz/home/jay/workspace)ConfigManagerTF)build_team_to_bot_id_mapdict[str, str]_TEAM_TO_BOT_IDc                 
   t         dz  dz  } | j                         si S 	 t        j                  | j	                  d            S # t        j
                  t        f$ r$}t        j                  d|        i cY d}~S d}~ww xY w)u+   config/constants.json 파일 직접 읽기.configconstants.jsonutf-8encoding   constants.json 읽기 실패: N)		WORKSPACEexistsjsonloads	read_textJSONDecodeErrorOSErrorloggerwarning)pathexcs     A/home/jay/workspace/.worktrees/task-2551-dev6/utils/bot_status.py_load_constants_jsonr    =   ss    x"22D;;=	zz$..'.:;;  '* 7u=>	s   $A BA=7B=Bc            	        t         rt        rt        S t        r*t        r$	 t        j	                  d      } | rt        |       S 	 t               }t        |j                  di             xs dddddddd	d
S # t        $ r Y =w xY w)uM   팀→봇 매핑을 반환. org_loader 우선, fallback으로 constants.json.team_to_botbot-bbot-cbot-dbot-ebot-fbot-gbot-hbot-iz	dev1-teamz	dev2-teamz	dev3-teamz	dev4-teamz	dev5-teamz	dev6-teamz	dev7-teamz	dev8-team)	_ORG_LOADER_AVAILABLEr   _CONFIG_AVAILABLE_cfgget_constantdict	Exceptionr    get)result	constantss     r   _resolve_team_to_bot_idr5   I   s    T	&&}5FF|# 
 %&I	mR01 		6 		  		s   !A5 5	B B)r#   r$   r%   r&   r'   r(   r)   r*   	list[str]ALL_BOTS>   designcontent	marketing
consulting
publishingzset[str]DYNAMIC_BOT_TEAMSr#   r$   r%   r&   r'   r(   r)   r*   r+   _FALLBACK_TEAM_TO_BOTc                      e Zd ZdZ	 ddd	 	 	 	 	 ddZddZddZddZddZdd	Z	dd
Z
dddZddZddZddZddZddZ	 d	 	 	 	 	 	 	 ddZy)BotStatusManageru   모든 봇의 현재 상태를 관리하는 단일 소스.

    task-timers.json 및 config/constants.json을 기반으로 봇 점유/유휴 상태,
    팀 라우팅 추천, 라우팅 검증 기능을 제공합니다.
    N)task_timersc                   |xs t         | _        | j                  dz  dz  | _        | j                  dz  dz  | _        || _        y )Nmemoryztask-timers.jsonr   r   )r   
_workspace_timer_file_constants_file_injected_task_timers)selfworkspace_rootrA   s      r   __init__zBotStatusManager.__init__   sF     )5I!%8!;>P!P%)__x%?BR%R5@"    c                   | j                   j                         si S 	 t        j                  | j                   j	                  d            S # t        j
                  t        f$ r$}t        j                  d|        i cY d}~S d}~ww xY w)uB   인스턴스 workspace 기준 constants.json 파일 직접 읽기.r   r   r   N)	rF   r   r   r   r   r   r   r   r   )rH   r   s     r   _load_constantsz BotStatusManager._load_constants   su    ##**,I	::d22<<g<NOO$$g. 	NN;C5ABI	s   .A B
&B?B
B
c                    | j                   t        k7  r3| j                         }t        |j	                  di             xs t
        S t               S )u6   인스턴스 workspace 기준 팀→봇 매핑 반환.r"   )rD   r   rM   r0   r2   r>   r5   )rH   r4   s     r   r5   z(BotStatusManager._resolve_team_to_bot_id   sC     ??i',,.I	mR89R=RR&((rK   c           	         | j                         }|j                         D ci c]  \  }}||j                  dd       c}}S c c}}w )u8   인스턴스 workspace 기준 봇→dev short id 매핑.-team )r5   itemsreplace)rH   r"   tidbots       r   _resolve_bot_to_devz$BotStatusManager._resolve_bot_to_dev   sA    224>I>O>O>QR(#sS[["--RRRs   Ac                l    | j                         }|D cg c]  }|j                  dd       c}S c c}w )u2   인스턴스 workspace 기준 dev short id 목록.rP   rQ   )r5   rS   )rH   r"   rT   s      r   _resolve_dev_short_idsz'BotStatusManager._resolve_dev_short_ids   s/    2244?@SGR(@@@s   1c                n   | j                   | j                   S | j                  j                         si S 	 t        j                  | j                  j                  d            }|j                  di       S # t        j                  t        f$ r$}t        j                  d|        i cY d}~S d}~ww xY w)u   task-timers.json의 tasks 섹션 반환. 실패 시 빈 dict.

        주입된 task_timers가 있으면 파일 읽기 없이 그것을 반환.
        Nr   r   tasksu    task-timers.json 읽기 실패: )rG   rE   r   r   r   r   r2   r   r   r   r   )rH   datar   s      r   _load_task_timersz"BotStatusManager._load_task_timers   s    
 %%1---&&(I	::d..88'8JKD88GR(($$g. 	NN=cUCDI	s   A A7 7B4B/)B4/B4c                    t         r*t        r$	 t        j                  d      }|rt        |      S 	 | j                         }|j                  di       S # t        $ r Y -w xY w)u   config/constants.json의 logical_teams 섹션 반환.

        ConfigManager 우선, fallback으로 파일 직접 읽기.
        logical_teams)r-   r.   r/   r0   r1   rM   r2   )rH   r3   r4   s      r   _load_logical_teamsz$BotStatusManager._load_logical_teams   se    
 **?;<' 
 ((*	}}_b11	  s   !A 	AAc                4   | j                         }| j                         }i }|j                         D ]b  \  }}|j                  d      dk7  r|r||k(  r#|j                  dd      }||v r||   }||d||<   |j                  dd      }	|	s[||d||	<   d |S )u  running 상태 태스크에서 점유된 봇 정보 반환.

        dev팀 running task → TEAM_TO_BOT_ID 매핑으로 봇 추출.
        논리적 팀(design 등) running task → bot 필드 직접 사용.

        Args:
            exclude_task_id: 결과에서 제외할 task_id (자기 자신 제외용).

        Returns:
            {"bot-b": {"task_id": "...", "team_id": "..."}, ...}
        statusrunningteam_idrQ   )task_idrc   rU   )r5   r\   rR   r2   )
rH   exclude_task_idr"   rZ   busyrd   entryrc   rU   	bot_fields
             r   get_busy_botszBotStatusManager.get_busy_bots   s     224&&(*,#kkm 	KNGUyy"i/7o#= 99Y3G +%!'*(/GDS	 #YYub1I.5'"JY!	K$ rK   c                    t        | j                         j                               }t        D cg c]	  }||vs| c}S c c}w )u   전체 봇 목록에서 busy 봇을 제외한 유휴 봇 ID 목록 반환.

        Returns:
            ["bot-c", "bot-f", ...] (우선순위 순서 유지)
        )setri   keysr7   )rH   rf   rU   s      r   get_idle_botszBotStatusManager.get_idle_bots   s:     4%%',,./';3d?;;;s
   	AAc                &    || j                         vS )u   특정 봇이 현재 유휴 상태인지 확인.

        Args:
            bot_id: 확인할 봇 ID (예: "bot-e").

        Returns:
            True이면 유휴, False이면 점유 중.
        )ri   )rH   bot_ids     r   is_bot_availablez!BotStatusManager.is_bot_available	  s     T//111rK   c                ^   | j                         }|j                         D ]-  }|j                  d      dk(  s|j                  d      |k(  s- y | j                         }|j                  |      }|r:| j	                         }|j                  dd      }||v r||   }d|d    d	|d
    dS y)ud  dev팀의 현재 상태 문자열 반환.

        - running task 있음 → "작업중"
        - 봇이 논리적 팀에 점유됨 → "봇점유({team}:{task_id})"
        - 그 외 → "유휴"

        Args:
            team_id: dev팀 ID (예: "dev4-team").

        Returns:
            "작업중" | "봇점유(design:task-123)" | "유휴"
        ra   rb   rc   u	   작업중rP   rQ   u
   봇점유(team:rd   )u   유휴)r\   valuesr2   r5   get_bot_occupationrS   )	rH   rc   rZ   rg   r"   ro   
occupation	dev_shortocc_infos	            r   get_team_statusz BotStatusManager.get_team_status  s     &&( \\^ 	#Eyy"i/EIIi4HG4S"	#
 224)002J4IJ&%i0#HV$4#5Qx	7J6K1MMrK   c                   | j                         }t        | j                               }| j                         }i }|j	                         D ]  \  }}|j                  d      dk7  r|j                  dd      }|j                  dd      }|sB|j                  dd      }	|	|v rY|j                  |      }
|
sm||j                  d|      |d||
<    |S )	u  논리적 팀이 물리 봇을 점유하는 경우를 탐지.

        dev팀 자체 작업은 제외하고, marketing/design/content 등 논리적 팀이
        dev봇을 빌려 사용하는 경우만 반환합니다.

        Returns:
            {dev_short_id: {"team": team_id, "task_id": task_id, "bot_id": bot_id}}
            예: {"dev4": {"team": "design", "task_id": "task-99", "bot_id": "bot-e"}}
        ra   rb   rc   rQ   rU   rP   rd   )rr   rd   ro   )rV   rk   rX   r\   rR   r2   rS   )rH   
bot_to_devdev_short_idsrZ   rw   rd   rg   rc   ro   
team_short	dev_owners              r   rv   z#BotStatusManager.get_bot_occupation4  s     --/
D779:&&(02
#kkm 	NGUyy"i/ 99Y3G))E2.F !"5J]* #v.I#$yyG<$)
9%#	. rK   c                ^   | j                         }|syd}d}|j                         D ]z  \  }}|dk(  rt        fdt        D              s$|j	                  dg       }|j	                  dg       }t        fd|D              r]t        fd|D              }	|	|kD  sw|	}|}| |dkD  r|S dS )	uv  작업 설명에서 키워드 매칭으로 적합한 논리적 팀 추천.

        constants.json의 logical_teams 키워드를 사용하며 anti_keywords가
        포함된 팀은 후보에서 제외합니다.

        Args:
            task_desc: 작업 설명 문자열.

        Returns:
            추천 팀 ID (예: "design") 또는 None (매칭 없음).
        Nr   	compositec              3  &   K   | ]  }|v  
 y wN ).0trigger	task_descs     r   	<genexpr>z0BotStatusManager.suggest_team.<locals>.<genexpr>u  s     ZG7i/Z   keywordsanti_keywordsc              3  &   K   | ]  }|v  
 y wr   r   )r   akr   s     r   r   z0BotStatusManager.suggest_team.<locals>.<genexpr>}  s     ;r2?;r   c              3  ,   K   | ]  }|v sd   yw)   Nr   )r   kwr   s     r   r   z0BotStatusManager.suggest_team.<locals>.<genexpr>  s     @bi@s   	)r_   rR   anyr   r2   sum)
rH   r   r^   	best_team
best_scorerc   r   r   r   scores
    `        r   suggest_teamzBotStatusManager.suggest_team\  s     002#'	
,224 	$OGV+%
 Z?YZZ $ZZ
B7H"(**_b"AM ;];; @H@@Ez!"
#	-	$0 'Ny44rK   c                    |t         v ry| j                  |      }|y|rt        j                  d| d| d       y| j	                         }|j                  |i       j                  dd      }d| d| d	S )
u  dev팀에 논리적 팀 소관 작업이 위임될 때 경고 메시지 반환.

        논리적 팀(design, marketing 등)에 직접 위임된 경우는 검증 생략.

        Args:
            team_id: 위임 대상 팀 ID (예: "dev1-team").
            task_desc: 작업 설명 문자열.
            override_routing: True이면 경고를 무시하고 None 반환.

        Returns:
            경고 메시지 문자열 또는 None (문제 없음).
        Nz[routing-override] u   에 u-    소관 작업 위임 (override_routing=True)descriptionrQ   u   이 작업은 --team u   이 적합합니다 (u<   ). 계속하려면 override_routing=True를 사용하세요.)r=   r   r   r   r_   r2   )rH   rc   r   override_routing	suggestedr^   r   s          r   validate_routingz!BotStatusManager.validate_routing  s    & ''%%i0	NN0	i[Huvw002#''	26::="M#I;.CK= QH I	
rK   r   )rI   zOptional[Path]rA   zOptional[dict]returnNoner   r0   r   r   )r   r6   )re   Optional[str]r   dict[str, dict[str, str]])ro   strr   bool)rc   r   r   r   )r   r   )r   r   r   r   )F)rc   r   r   r   r   r   r   r   )__name__
__module____qualname____doc__rJ   rM   r5   rV   rX   r\   r_   ri   rm   rp   rz   rv   r   r   r   rK   r   r@   r@      s     *.	A '+		A&	A $		A
 
	A)S
A
 2("H<	2@&P+5b "'	#
#
 #
 	#

 
#
rK   r@   r   r   )%r   
__future__r   r   ospathlibr   typingr   utils.composite_constantsr   utils.loggerr   r   r   environr2   r   config.loaderr	   _ConfigManagerget_instancer.   r-   r1   utils.org_loaderr
   _build_team_to_bot_id_mapr   __annotations__r,   r    r5   r7   r=   r>   r@   r   rK   r   <module>r      s  " #  	   @ #	H	  02GHI	
=&>&&(D"V&?&AO^A 	<	) 	 ] 8 \ 	) ~ 	"g
 g
s  D  "O!"s$   B8 /C 8	CC	CC