
    9'iN7                       U d Z ddlmZ ddlZddlmZmZ ddlmZ ddl	m
Z
mZ ddlmZmZ d	d
ddddZded<    ed      Zdaded<   ddZddZe G d d             Z	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddZddZ	 	 d	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d dZy)!u_  retry_loop.py — IDS Phase 1+2 retry-until-pass 루프.

목적:
  quality_evaluator.evaluate_image() FAIL 시 자동 재시도하여
  PASS 이미지만 최종 산출. silent pass 절대 금지.

회장 3원칙:
  - 더 가볍게: 외부 API 호출 0
  - 더 정확하게: 코드 수치 기반 PASS/FAIL 판정
  - 더 퀄리티 높게: 5회 retry-until-pass, 실패 시 RuntimeError 발생
                     (운영팀 알림 의무)

CRITICAL: silent pass 절대 금지.
  5회 retry 후 PASS 미달 시 반드시 RuntimeError를 raise한다.
  예외를 삼키거나 부분 결과를 반환하는 것은 금지.
  5회 retry 후 ERROR 발생 시 운영팀 핸들링 의무.

Phase 2 통합 (task-2428): _render_with_seed가 hybrid-image PATTERNS dict를
직접 dispatch한다. 6축 hint는 design_tokens에 'hint_*' prefix로 주입된다.
패턴은 모르는 hint 키를 graceful 무시하므로 회귀 0이 보장된다.

  - seed: random_seed (배경/레이아웃 변형)
  - hints["force_pattern_diversity"]: 단조 패턴 강제 회피
  - hints["force_brand_color"]: design-md primary 색 강제 적용
  - hints["force_pattern_signature"]: 5 hybrid 패턴별 특징 강제
  - hints["min_font_px"]: 폰트 크기 강제 임계
  - hints["force_korean_font"]: 한글 폰트 강제 (fallback 차단)
  - hints["force_spatial_coherence"]: 공간 일관성 강제
    )annotationsN)	dataclassfield)Path)AnyCallable   )
EvalResultevaluate_imageh1h2h3h4h5)h1_photo_cardh2_illustration_cardh3_gpt_style_cardh4_gradient_cardh5_user_photo_cardzdict[str, str]_PATTERN_NAME_MAPz0/home/jay/workspace/skills/hybrid-image/patternsz
Any | None_PATTERNS_MODULE_CACHEc                    t         t         S ddl} ddl}t        dz  }|j	                         st        d|       d}||j                  v r|j                  |   a t         S | j                  j                  ||t        t              g      }||j                  t        d|       | j                  j                  |      }||j                  |<   |j                  j                  |       |a |S )u  hybrid-image/patterns 모듈을 file-based importlib로 로드한다.

    skills/hybrid-image 디렉토리 이름에 하이픈이 있어 dotted import 불가능 →
    importlib.util.spec_from_file_location으로 우회한다.
    동일 프로세스 내 1회 캐시.
    Nr   z__init__.pyu+   hybrid-image patterns 디렉토리 부재: _hybrid_patterns_pkg)submodule_search_locationsu   importlib spec 생성 실패: )r   importlib.utilsys_HYBRID_PATTERNS_DIRexistsRuntimeErrormodulesutilspec_from_file_locationstrloadermodule_from_specexec_module)	importlibr   	init_pathpkg_namespecmodules         @/home/jay/workspace/skills/satori-cardnews/scripts/retry_loop.py_load_hybrid_patterns_moduler-   ;   s     )%%$}4I9)E
 	
 &H3;;!$X!6%%>>11$'(<$=#> 2 D
 |t{{*;I;GHH^^,,T2F"CKKKKF##M    c           	         | t         vr-t        d|  dt        t         j                                d      t         |    }t	               }|j
                  |   S )u   hybrid-image PATTERNS dict에서 render 함수를 가져온다.

    Lazy import — retry_loop를 단위 테스트 시 hybrid-image 의존 회피.
    zunknown hybrid_pattern: z (supported: ))r   
ValueErrorlistkeysr-   PATTERNS)pattern_full_name	short_keypatterns_modules      r,   _get_pattern_render_fnr8   e   sj    
  11&'8&9 : 1 6 6 89:!=
 	
 ""34I24O##I..r.   c                  V    e Zd ZU dZded<   ded<   ded<   ded	<    ee
      Zded<   y)RetryResultu!   generate_with_eval 반환 결과.boolsuccesszPath | None
final_pathintattemptsr
   
final_eval)default_factoryzlist[dict[str, Any]]historyN)__name__
__module____qualname____doc____annotations__r   r2   rB    r.   r,   r:   r:   x   s+    +MM$)$$?G!?r.   r:   c                   t        |      }|j                  d      xs |j                  d      xs d}|j                  dd      |j                  dd      |t        |      t        dt	        |j                  d	d
      xs d
            t        d|j                  d	      r"t	        |j                  d	d
      xs d
      dz
  nd      |t        |j                  dd            |t        |j                  dd            t	        |j                  d	d
      xs d
      t        |j                  dd            t        |j                  dd            |d}	t        | j                  dd            }
t        | j                  dd            }| j                  d      }| j                  d      }	  ||
||||	||       |j                         st        d| d | d| d!      y# t        $ r}t        d| d| d| d|      |d}~ww xY w)"u#  hybrid 패턴으로 PNG를 렌더링한다 (task-2428 Phase 2-1 통합 완료).

    skills/hybrid-image/patterns/PATTERNS dict를 직접 dispatch한다.
    6축 hint는 design_tokens에 'hint_*' prefix로 주입되며, 패턴은
    모르는 키를 graceful 무시한다. (회귀 0)

    Args:
        content: {"title": str, "body": str, "background_path": Path | None,
                  "prompt_hint": str | None} 카드뉴스 콘텐츠
        design_md: {"primary": str, "secondary": str, "name": str} 브랜드 정보
        hybrid_pattern: 'h1_photo_card' | 'h2_illustration_card' |
                        'h3_gpt_style_card' | 'h4_gradient_card' | 'h5_user_photo_card'
        target_size: (width, height) 픽셀
        output_path: 출력 PNG 경로
        seed: 랜덤 시드 (attempt별 변형값)
        hints: 이전 evaluate_image의 retry_hints (패턴/색상 강제 힌트 포함)

    Raises:
        ValueError: hybrid_pattern이 지원되지 않는 경우.
        RuntimeError: PATTERNS dict 호출 실패 (silent pass 차단).
    force_brand_colorprimaryz#0f1729title_colorz#fafaf8
body_colorz#d4d8e0H   min_font_pxr   &      force_pattern_diversityFforce_pattern_signatureforce_korean_fontTforce_spatial_coherence)rL   rM   accent_colorgradient_theme
title_size	body_size	hint_seedhint_force_pattern_diversityhint_force_brand_colorhint_force_pattern_signaturehint_min_font_pxhint_force_korean_fonthint_force_spatial_coherenceprimary_hextitle bodybackground_pathprompt_hint)sizedesign_tokensre   rf   u'   hybrid-image 렌더링 실패: pattern=z, seed=z	, output=z, error=Nu!   렌더링 후 출력 PNG 부재: z
 (pattern=r0   )
r8   get_select_gradient_thememaxr>   r;   r#   	Exceptionr   r   )content	design_mdhybrid_patterntarget_sizeoutput_pathseedhints	render_fnbrand_colorrh   rb   rd   re   rf   excs                  r,   _render_with_seedrw      s$   < '~6I 			%& 	==#	  !}}]I>mmL)<#06"c%))M1"="BCD599UbKcS=!!<!ABRGikl(,UYY7PRW-X(Y"-(,UYY7PRW-X(Y		- ; @qA"&uyy1Dd'K"L(,UYY7PRW-X(Y"'%M, GR()Ew{{62&'Dkk"34O++m,K'+#	
" /} =&'wtfA7
 	
    5n5E F6;-xw@
 	s   ,G! !	H*HHc                ,    g d}|| t        |      z     S )u   seed 기반으로 gradient theme를 회전 선택한다 (h4 단조 회피).

    H4 gradient_card 패턴은 design_tokens["gradient_theme"]을 소비한다.
    동일 seed → 동일 theme (결정성 보장).
    )navywarmmintrosemono)len)rr   themess     r,   rj   rj      s     6F$V$%%r.   c           
        ||nt        j                  dd      }g }i }	d}
t        d|dz         D ]  }||dz  z   }d}	 t        | ||||||	       |r%||t        |	      d|g|d}|j                  |       Gt        ||||	      }|}
||t        |	      |j                  t        |j                        d
}|j                  |       |j                  rt        d||||      c S |	j                  |j                          |
r|
j                  ndg}dj!                  |      }t	        d| d| d|
r|
j                  nd d|       # t        t
        f$ r}d|}Y d}~d}~ww xY w)u  hybrid 패턴으로 PNG를 생성하고 품질 평가 PASS까지 재시도한다.

    CRITICAL — silent pass 절대 금지:
      max_retry 회 내 PASS 미달 시 RuntimeError를 raise한다.
      예외를 삼키는 것은 절대 금지. 5회 retry 후 ERROR 발생 시 운영팀 알림 의무.

    Args:
        content: {"title": str, "body": str, ...} 카드뉴스 콘텐츠
        design_md: {"primary": str, "secondary": str, "name": str} 브랜드 정보
        hybrid_pattern: 'h1_photo_card' | 'h2_illustration_card' |
                        'h3_gpt_style_card' | 'h4_gradient_card' | 'h5_user_photo_card'
        target_size: (width, height) 픽셀
        output_path: 최종 출력 PNG 경로
        max_retry: 최대 재시도 횟수 (기본 5회)
        initial_seed: 초기 랜덤 시드 (None이면 자동 생성)

    Returns:
        RetryResult (success=True 보장, 실패 시 RuntimeError raise)

    Raises:
        RuntimeError: max_retry 회 내 PASS 미달 시 — 운영팀 핸들링 의무
    Nr   l        r	   i  )rm   rn   ro   rp   rq   rr   rs   u   렌더링 실패: )attemptrr   rs   scorefail_reasonsrender_error)png_pathrn   ro   rp   )r   rr   rs   r   r   T)r<   r=   r?   r@   rB   u!   렌더링 실패로 평가 불가z; zretry-until-pass FAILED after z attempts: ub   
[운영팀 알림 의무] 5회 retry 후 PASS 미달 — 수동 개입 필요.
마지막 점수: z/100
history: )randomrandintrangerw   r   r1   dictappendr   r   r2   r   passedr:   updateretry_hintsjoin)rm   rn   ro   rp   rq   	max_retryinitial_seedrr   rB   rs   	last_evalr   attempt_seedr   rv   recordeval_resultfinal_fail_reasonsfail_summarys                      r,   generate_with_evalr      s   @ (3<59QD$&GE#'I IM* A.gn,
 $(	8#-''! "$e!- ,&F NN6" % )#	
  	  %[ && !9!9:
 	v & &  	[,,-CA.N 4=//CfBg99/0L

(;|n M09Y__qA B9	 q j) 	8/w7L	8s   E		E(E##E()returnr   )r5   r#   r   zCallable[..., Path])rm   dict[str, Any]rn   r   ro   r#   rp   tuple[int, int]rq   r   rr   r>   rs   r   r   None)rr   r>   r   r#   )   N)rm   r   rn   r   ro   r#   rp   r   rq   r   r   r>   r   z
int | Noner   r:   )rF   
__future__r   r   dataclassesr   r   pathlibr   typingr   r   quality_evaluatorr
   r   r   rG   r   r   r-   r8   r:   rw   rj   r   rH   r.   r,   <module>r      s@  < #  (    9  % >  NO %) 
 )'T/& @ @ @Y
Y
Y
 Y
 !	Y

 Y
 Y
 Y
 
Y
x&( #vvv v !	v
 v v v vr.   