
    Oj#"                     R   d Z ddlZddlZddlZddlmZmZmZmZm	Z	m
Z
 ddlmZ ddlmZ  ej                  e      ZdZddiZd	Zd
ZdedefdZdeeef   dee   fdZdeeef   deeef   fdZdeeef   defdZdeeef   defdZdeeef   de
e   dee   fdZdedeeef   fdZdedeeef   deeef   dededeeeef      fdZdee   defdZ ddddded deeef   deeef   deded!ed"ed#e	ed$eeef   f      d%e	eeef      d&e	eeef      d'e!deeef   fd(Z"y))uA  outdoor_shot_grounding — 샷 grounding (W22 직행 체인 ③, s30 stage_ground 계약).

야외 선택 샷마다 캐논 맵+마커 범례+샷 원문+**씬 원문 전문**을 주고
존/앵커 마커/카메라 배치를 접지한다. s30 대비 변경(사용자 확정):
  - concept 선행 콜 없음 (1콜 축약 — 씬 원문이 구상 컨텍스트를 대신)
  - moment_context_en 신설: 씬 원문에만 있는 순간의 세계 사실 보강
    (S13_3 '차'→경찰차 소실 사례 대응, evidence-bound·연출 금지)

스키마는 스펙에서 동적 생성 — map_zone/anchor_markers 가 해당 그룹의
존 라벨/마커 코드 enum 으로 잠긴다. 서술 필드는 ID-free (코드는
anchor_markers 에만) — 결정론 검증+재시도.
설계: docs/w22-outdoor-canon-direct-compose-design-20260709/design.md
    N)AnyCallableDictListOptionalSequence)AppError)load_promptoutdoor_shot_grounding1z1.202607100125   )camera_position_enlook_direction_enin_frame_enmoment_context_enselectorreturnc           	      n    	 t         |    S # t        $ r  t        d| dt        t                d      w xY w)Nz7unknown outdoor_shot_grounding prompt version selector z	 (known: ))PROMPT_VERSION_MAPKeyError
ValueErrorsorted)r   s    a/Users/manta/Documents/Projects/TheRoad-I1/backend/app/modules/pipeline/outdoor_shot_grounding.pyresolve_prompt_versionr   *   sK    
!(++ 
El)F+=$>#?qB
 	

s    )4specc                     | j                  dg       xs g D cg c]%  }|j                  d      r|j                  dd      ' c}S c c}w )Nitemscode get)r   its     r   
spec_codesr$   4   sP    %)XXgr%:%@b%@%@r66&> 	vr%@  s   *Ac                     t        | j                  dg       xs g       }t        |       }|r|st        ddd      ddg dd	|d
ddd	|d
dd	ddd	ddd	ddd	dddd	iddS )uR   그룹 스펙에서 동적 enum 스키마 생성 (s30 계약 + moment_context_en).zone_labels_enz$step.outdoor_shot_grounding.bad_specuK   스펙에 존 라벨 또는 마커 코드가 없어 스키마 생성 불가  r   messagestatus_codeobjectF)map_zoneanchor_markersr   r   r   rationale_kor   string)typeenumarray   )r0   minItemsr      )r0   	minLength   r0   )r0   additionalPropertiesrequired
properties)listr"   r$   r	   )r   zonescodess      r   build_ground_schemar>   ;   s    *B/526EtE7a
 	
  %
 "*59"*E:
 ,4!"D*2!C$,1=%-A>"((!3
     c                 Z    dj                  d | j                  dg       xs g D              S )uV   VLM 내부용 코드 포함 범례 (s30 계약 — 하류 t2i 프롬프트와 무관).
c           
   3      K   | ]?  }d |j                  dd       d|j                  dd       d|j                  dd        A yw)z- (r   r    z) name_enu    — placement_enNr!   ).0r#   s     r   	<genexpr>z%build_legend_block.<locals>.<genexpr>_   sT       .B bffVR !BFF9b$9#:%66."%
&	(-s   AAr   )joinr"   )r   s    r   build_legend_blockrH   ]   s5    99  ((7B'-2-  r?   shotc           	      R   | j                  d      }| j                  d      }d| d| d| j                  dd       g}| j                  d      xs g }|r*|j                  d	d
j                  d |D                      | j                  d      xs d}|r|j                  d|        | j                  d      xs g D cg c]%  }|j                  d      r|j                  dd      ' }}|r#|j                  dd
j                  |              dj                  |      S c c}w )Nscene_index
shot_indexS_Shotz: descriptionr    
charactersu   등장 인물: z, c              3   2   K   | ]  }t        |        y w)N)str)rE   cs     r   rF   z#build_shot_block.<locals>.<genexpr>l   s     0GAQs   camera_directionu"   연출 카메라 메모 (참고): key_bg_elementselementu   연출 배경 요소 (참고): rA   )r"   appendrG   )rI   sishilinescharscamelelementss           r   build_shot_blockr_   f   s)   	-	 B
((<
 CE#b-!< =>?EHH\"(bEtyy0G0G'G&HIJ
((%
&
,"C
9#?@(,1B(C(Ir(I(I"66) 	y"(I   6tyy7J6KLM99Us   *D$resultr=   c           
         g }t         D ]o  }| j                  |      xs d}|D cg c]3  }|r/t        j                  dt        j                  |       d|      r|5 }}|sY|j                  | d| d       q |S c c}w )ub   서술 필드 ID-free 결정론 검증 — 위반 리스트 반환 (enum 은 스키마가 잠금).r    z\bu    에 마커 코드 uT    포함 — 서술명(name_en)으로 지칭할 것 (코드는 anchor_markers 에만))_PROSE_FIELDSr"   researchescaperW   )r`   r=   
violationsfieldtextrS   leakeds          r   validate_groundrj   y   s     Jzz% &B
!RYY"RYYq\N"5t< u 	 
 ',VH 5B B  
s   8A=	png_bytesc                 \    t        j                  |       j                         }ddd| idS )N	image_urlurlzdata:image/png;base64,)r0   rm   )base64	b64encodedecode)rk   b64s     r   	_png_partrs      s:    


9
%
,
,
.C#9#!?@B Br?   template
scene_textmap_pngc                   | }t        |      dj                  d |j                  dg       xs g D              t        |      |dj	                         D ]  \  }}|j                  d|z   dz   |      } ddd	t        |      d|d	gS )
uP   멀티모달 user 조립 — s30 파트 순서(맵→범례→샷→씬 전문).rA   c              3   &   K   | ]	  }d |   yw)z- N )rE   zs     r   rF   z#build_user_parts.<locals>.<genexpr>   s      !
BbHB   r&   )legend_blockzones_block
shot_blockscene_text_block{}rh   z"SITE PLAN of the filming property:r0   rh   )rH   rG   r"   r_   r   replacers   )rt   r   rI   ru   rv   filledkeyvals           r   build_user_partsr      s     F*40yy !
"hh'7<BB!
 
 't,& egS c	C5 !EF'( r?   rf   c                 \    g d}|j                  d | D               dj                  |      S )N)r    r    uU   [재시도 — 직전 응답이 아래 계약을 위반했습니다. 전부 고쳐서u*    전체 결과를 다시 출력하세요:]c              3   &   K   | ]	  }d |   yw)z  - Nry   )rE   vs     r   rF   z%_format_retry_hint.<locals>.<genexpr>   s     0Z4sZr{   rA   )extendrG   )rf   rZ   s     r   _format_retry_hintr      s,    E 
LL0Z0099Ur?   r    )creator_corrections_blockprompt_versioncall_structured_fnproject_configopik_metadatamax_attemptsr   r   r   .r   r   r   c        
   
         |ddl m}
 |
}t        |      }t        t        d|      }||xs dz  }t        t        d|      }t        |       }t        |       }t        || |||      }d}|}g }||	k  r|d	z  } |d
||||d
|      }t        |xs i |      }|s||dS |}t        j                  d|j                  d      |j                  d      |t        |             |dt        |      dgz   }||	k  rt        dd|j                  d       d|j                  d       d|	 ddj                  |dd       z   d      )u   샷 1개 grounding — ID-free 위반 시 위반 힌트 재시도.

    반환 {"ground": <검증 통과 결과>, "attempts": n}.
    소진 시 AppError(step.contract_violation.outdoor_shot_grounding).
    Nr   )call_structuredsystem)versionr    user_template)r   rI   ru   rv   r3   r   )r   schema_namer   )groundattemptsz:outdoor_shot_grounding S%s_Shot%s attempt=%d violations=%drK   rL   rh   r   z.step.contract_violation.outdoor_shot_groundingrM   rN   u#    grounding 계약 위반 (attempts=z): z; r5   r'   r(   )app.modules.llm.llm_clientr   r   r
   _MODULEr>   r$   r   rj   loggerwarningr"   lenr   r	   rG   )r   rI   ru   rv   r   r   r   r   r   r   r   resolvedr   rt   schemar=   
base_partsr   partslast_violationsr`   rf   s                         r   run_outdoor_shot_grounding_shotr      s   $ !>,%n5H(H=F
'-2-F7OXFH &FtE!t$:wJ HE!#O
\
!A#$)0'
 %V\r59
$(;;$HHH]#TXXl%;c*o	

 %7
%CD
 
) \
!0 ='(dhh|.D-E F11=cCii+,-  r?   )#__doc__ro   loggingrc   typingr   r   r   r   r   r   app.core.errorsr	   app.modules.prompt_loaderr
   	getLogger__name__r   r   r   MAX_ATTEMPTSrb   rR   r   r$   r>   rH   r_   rj   bytesrs   r   r   intr   ry   r?   r   <module>r      sz     	 @ @ $ 1			8	$
" 	  
S 
S 
T#s(^ S	 d38n c3h DT#s(^  4S> c &cN#+C=	#Y&B B4S> B sCx. sCx.	
   
$sCx.649  " &(BF/3.2$E
sCx.E sCx.E 	E
 E  #E E !#tCH~*=!>?E T#s(^,E DcN+E E 
#s(^Er?   