
    XjI              
         U d Z ddlmZ ddlZddlZddlmZmZmZm	Z	 ddl
mZ  ej                  e      Zg dg dg dg d	g d
g dg dg dg dd	Zded<   ddgddgddgdZded<   dZdZ eg d      Z eg d      Z eg d      Z ej0                  d      Z ej0                  d       Zd'd!Z	 	 	 	 	 	 d(d"Z	 	 	 	 	 	 d)d#Z	 	 	 	 	 	 d*d$Z	 	 	 	 	 	 d+d%Zd,d&Z y)-u  frame_spatial_contract — opt-in shot 의 화면 좌표/방향 SOT helper.

Spec: docs/superpowers/specs/2026-05-14-frame-spatial-contract-design.md

5 함수 (caller 분리):
  - find_frame_spatial_contract_violations(batch_shots): shot_staging producer
    가 retry hint 형성용 violation list 반환. raise X.
  - validate_and_prepare(contract, visible_entities): render_prompt_card 가
    constraint_id 부여 + cross-check. invalid 시 AppError raise.
  - validate_echoes(card, result): scene_detail post-validation 이 per-
    variation echo set 검증. violations list 반환. raise X (caller retry 판단).
  - phrase_diagnostic(t2i_prompt, constraints): soft warning data 반환. raise X.
  - _format_retry_hint(fsc_violations): retry hint formatter (orientation
    pattern mirror).
    )annotationsN)AnyDictListOptional)AppError)z
upper-leftztop-leftz
upper left)zupper-centerz
top-centerzupper center)zupper-rightz	top-rightzupper right)zmiddle-leftzcenter-leftzmiddle leftz	left side)zmiddle-centercentermiddle)zmiddle-rightzcenter-rightzmiddle rightz
right side)z
lower-leftzbottom-leftz
lower left)zlower-centerzbottom-centerzlower center)zlower-rightzbottom-rightzlower right)	
upper_leftupper_centerupper_rightmiddle_leftmiddle_centermiddle_right
lower_leftlower_centerlower_rightzDict[str, List[str]]ZONE_PHRASES
foregroundfront	midgroundzmiddle ground
backgroundback)r   r   r   DEPTH_PHRASESfsc_   )movement_directionpoints_to_anchorlooks_to_anchorshared_space_relationrequired_background_positionprimary_subject_isolation)	characterpropr   )none	points_toreaches_forlooks_towardmoves_towardz
^C\d{2,3}$z
^P\d{2,3}$c           
        g }| xs g D ]j  }|j                  d      }|j                  d      }|j                  d      }|:t        |t              s|j                  ||dd       `|j                  d      }|t        vr|j                  ||d|d       |j                  d	      }t        |t
              rt        |      d
k(  r|j                  ||dd       t        |      t        kD  r|j                  ||dt        |      d       t        |      D ]W  \  }}	t        |	t              s|j                  |||dd       .|	j                  d      }
|	j                  dd      }|	j                  dd      }|	j                  d      }|	j                  d      }|	j                  d      }|	j                  dd      }|
t        vr|j                  |||d|
d       n|
dk(  r1t        j                  |xs d      s|j                  |||d|d       nW|
dk(  r1t        j                  |xs d      s|j                  |||d|d       n!|
dk(  r|dk7  r|j                  |||d |d       t        |t              r|j                         s|j                  |||d!d       |t        vr|j                  |||d"|d       |t         vr|j                  |||d#|d       |t"        vr|j                  |||d$|d       |d%k7  r6t        |t              r|j                         s|j                  |||d&d       |d%k(  s,|xs dj                         sB|j                  |||d'd       Z m |S )(u7   shot_staging producer: violation list 반환 (raise X).scene_index
shot_indexframe_spatial_contractcontract_not_dict)r+   r,   reasonr/   invalid_reason_enum)r+   r,   r/   valueconstraintsr   constraints_empty_or_not_listconstraints_max_3_exceeded)r+   r,   r/   countconstraint_not_dict)r+   r,   constraint_idxr/   target_kind	target_id labelscreen_zonedepth_planegesture_actiongesture_target_labelinvalid_target_kind)r+   r,   r7   r/   r1   r#   'target_kind_character_id_shape_mismatch)r+   r,   r7   r/   r9   r$   "target_kind_prop_id_shape_mismatchr   "target_kind_background_id_nonemptylabel_emptyinvalid_screen_zoneinvalid_depth_planeinvalid_gesture_actionr%   .gesture_target_label_empty_for_non_none_action-gesture_target_label_nonempty_for_none_action)get
isinstancedictappend_VALID_REASONlistlen_FSC_MAX_CONSTRAINTS	enumerate_VALID_TARGET_KIND_CHARACTER_ID_REmatch_PROP_ID_REstrstripr   r   _VALID_GESTURE_ACTION)batch_shotsoutshotsishicontractr/   r2   idxctktidr;   zonedepthgagtls                    U/Users/manta/Documents/Projects/TheRoad-I1/backend/app/core/frame_spatial_contract.py&find_frame_spatial_contract_violationsri   ?   s    "C!r!XXm$hh|$8845(D)JJrH[\]h'&JJrH]!') * ll=1+t,K0@A0EJJrHghi{22JJr">[IY[ \  ,FCa&

2STW&;= >}%B%%R(CEE'2&E55'DEE-(E'(B%%.3C++

2STW&;bJ K{"+;+A+A#)+L

2STW&O^ac dvk&7&7	r&B

2STW&JY\^ _|#r	

2STW&JY\^ _ uc*u{{}

2STW&35 6 <'

2STW&;dL M M)

2STW&;eM N ..

2STW&>M N <C)=#))+JJrX[*Z \ ]<SYB$5$5$7JJrX[*Y [ \_ -/ "P J    c                B   | t        | t              s| S | j                  d      }t        |t              s| S t	        |xs g       }g }|D ]  }t        |t              r|j                  d      }|j                  dd      xs d}|dk(  r:t
        j                  |      r%||vr!t        j                  d|t        |             y|dk(  r:t        j                  |      r%||vr!t        j                  d	|t        |             |j                  |        |syi | d|iS )
u  FINDING 6 W4a — consumer-boundary normalization (render_prompt_card path).

    shot_staging 이 emit 한 frame_spatial_contract constraints 중 character /
    prop target 이 shot-level visible set 에 없는 constraint 를 deterministic
    drop 한다. `shot_director.visible_entity_ids` 가 SOT — depicted-but-not-
    present target (침입손 / 유리 반사상) + shot-level visibility narrowing 으로
    target 이 visible 밖이라 `validate_and_prepare` 가 `fsc_cross_check_failed`
    로 raise 하던 것을 차단한다.

    drop 대상 = target_kind ∈ {character, prop} 이고 target_id 가 well-formed
    C##/P## 이면서 visible set 에 없는 constraint 만. background-kind constraint
    + visible character/prop constraint 는 **보존**. target_kind 미상 /
    target_id shape 위반 등 malformed constraint 도 보존 — 직후
    `validate_and_prepare` 가 fail-fast 하도록.

    모든 constraint 가 drop 되면 None 반환 (no-contract — card path 의 nullable
    frame_spatial_contract 값과 정합). contract 가 None / non-dict /
    constraints non-list 면 그대로 pass-through (`validate_and_prepare` 가 처리).
    Nr2   r8   r9   r:   r#   zaframe_spatial_contract: dropping constraint for non-shot-visible character target %s (visible=%s)r$   z\frame_spatial_contract: dropping constraint for non-shot-visible prop target %s (visible=%s))rK   rL   rJ   rO   setrT   rU   loggerwarningsortedrV   rM   )r_   visible_entitiesr2   visible_setkeptra   rb   rc   s           rh   !filter_fsc_constraints_to_visiblers      s!   . z(D9,,}-Kk4(&,"-K!#Da}%B%%R(.BC[ %5%;%;C%@SP[E[H,
 V| 1 1# 63k;QC,
 A% & ,h,t,,rj   c           
        | y|t        dd      t        | t              s$t        ddt        |       j                   d      | j                  d      }| j                  d      xs g }|t        vrt        dd	|      t        |t              rd
t        |      cxk  r	t        k  sn t        ddt               d,d}t        ||      }t               }|D ]/  } ||      }||v rt        dd|       |j                  |       1 t        |      }	g }
t        |      D ]0  \  }}|j                  d      }|j                  dd      xs d}|j                  d      xs dj                         }|j                  d      }|j                  d      }|j                  d      }|j                  dd      }|t        vrt        dd|      |st        dd      |t         vrt        dd|      |t"        vrt        dd|      |t$        vrt        dd|      |dk7  r-t        |t&              r|j                         st        dd      |dk(  r!|xs dj                         rt        dd      |dk(  rGt(        j+                  |      st        dd |d!      ||	vrt        d"d#|d$t        |	       d!      |d%k(  rGt,        j+                  |      st        dd&|d!      ||	vr8t        d"d#|d$t        |	       d!      |d'k(  r|dk7  rt        dd(|d!      |
j/                  d)t0         |d
z   d*i|       3 ||
d+S )-u  render_prompt_card consumer: shape validate + constraint_id assign +
    visible_entities cross-check. invalid 시 raise AppError.

    visible_entities 는 SID list (C##, P##, P###...). 기존 build_render_prompt_card
    signature 와 일관 (render_prompt_card.py:1020/3518).
    Nzrender_prompt_card.fsc_invalidu   validate_and_prepare: visible_entities is None — caller must pass explicit [] for intentionally empty (matches build_id_policy convention at render_prompt_card.py:1061-1068).)codemessagezframe_spatial_contract is z, expected dict or Noner/   r2   zinvalid reason    zconstraints must be list of 1..c           
     $   | j                  dd      | j                  dd      xs d| j                  dd      xs d| j                  dd      xs d| j                  dd      xs d| j                  dd      xs d| j                  dd      xs dfS )	Nr8   r:   r9   r;   r<   r=   r>   r?   )rJ   )ra   s    rh   	_sort_keyz'validate_and_prepare.<locals>._sort_key   s    EE-$EE+r"(bEE'2$"EE-$*EE-$*EE"B'-2EE("-3
 	
rj   )keyz.duplicate_constraint (same 7-tuple sort key): r8   r9   r:   r;   r<   r=   r>   r?   zinvalid target_kind zlabel is emptyzinvalid screen_zone zinvalid depth_plane zinvalid gesture_action r%   z:gesture_target_label is empty but gesture_action != 'none'z>gesture_target_label is non-empty but gesture_action == 'none'r#   z2target_kind=character requires C## target_id (got )z)render_prompt_card.fsc_cross_check_failedz
target_id z not in visible_entities (r$   z-target_kind=prop requires P## target_id (got r   z5target_kind=background requires empty target_id (got constraint_id03d)r/   r2   )ra   Dict[str, Any])r   rK   rL   type__name__rJ   rN   rO   rP   rQ   ro   rl   addrR   rX   rS   r   r   rY   rW   rT   rU   rV   rM   _FSC_ID_PREFIX)r_   rp   r/   r2   ry   sorted_constraints	seen_keysra   krq   prepared_constraintsr`   rb   rc   r;   rd   re   rf   rg   s                      rh   validate_and_preparer      s%     1B
 	
 h%10h1H1H0II`a
 	

 \\(#F,,}-3K]"<!0
;= 	=k4(c+6F1^J^1^<!@AU@VWY 	Y	
  ; UIaL	>5HL  	a   &'K./QUU=!eeK$*w%2,,.uu]#m$UU#$ee*B/ '' @%9"#@B B @#35 5|# @%9$#BD D% @%9%#CE E** @%<RF#CE E<C!5#))+ @#_a a<SYB--/ @#ce e
 #))#.9PQTPWWXY  +%D$SG+Ef[FYEZZ[\  6\$$S)9KC7RST  +%D$SG+Ef[FYEZZ[\  <by9STWSZZ[\ 
 	##/a}=%
%
 	I 0R -ABBrj   c                   | xs i j                  d      xs i }|j                  d      }|t               }n%|j                  d      xs g D ch c]  }|d   	 }}g }|xs i j                  dg       xs g D ]v  }t        |j                  d      xs g       }||k7  s'|j                  |j                  d      t        |      t        |      t        ||z
        t        ||z
        d       x |S c c}w )	uz   scene_detail post-validation: per-variation echo set 검증. violation
    list 반환 (raise X). caller 가 retry 판단.render_strategyr-   r2   r|   t2i_variations$applied_frame_spatial_constraint_idsvariant_label)r   injected_ids
echoed_idsmissingextra)rJ   rl   rM   ro   )	cardresultrsr_   r   ra   r[   vechoeds	            rh   validate_echoesr   S  s    *"		+	,	2Bvv./HE5=\\-5P5VTV5VX5Vq/*5VX "Cl 0"5;;QUUABHbI\!JJ!"!7 &| 4$Vn!,"78 56  < J Ys   C4c                   g }| xs dj                         |xs g D ]  }|j                  d      xs dj                         }|j                  d      }|j                  d      }t        j                  |g       }t        j                  |g       }t	        |      xr |v }	t        fd|D              }
t        fd|D              }|	r|
r|r|j                  |j                  d      |	 |
 | d        |S )	uC   soft warning data 반환 (raise X). v1 soft, v2 hard 승격 가능.r:   r;   r<   r=   c              3  &   K   | ]  }|v  
 y wN .0r   prompt_lowers     rh   	<genexpr>z$phrase_diagnostic.<locals>.<genexpr>|  s     Dm1,m   c              3  &   K   | ]  }|v  
 y wr   r   r   s     rh   r   z$phrase_diagnostic.<locals>.<genexpr>}  s     F~!A-~r   r|   )r|   label_missingzone_missingdepth_missing)lowerrJ   r   r   boolanyrM   )
t2i_promptr2   r[   ra   r;   rd   re   zone_variantsdepth_variantslabel_presentzone_presentdepth_presentr   s               @rh   phrase_diagnosticr   n  s    
 !#C$"++-LBw%2,,.uu]#m$$((r2&**5"5U=(=DmDDF~FF,=JJ!"!7%2!2$0 0%2!2	    Jrj   c                p   g d}| D ]  }|j                  d      }|j                  d      }|j                  d      }|j                  d      }d| d| }|	|d| d	z  }d
}d|v r
d|d   d}nd|v r
d|d   d}nd|v r	d|d    d}|j                  d| d| |         dj                  |      S )u[   retry hint formatter — shot_staging.py:_format_retry_hint orientation
    pattern mirror.)r:   r:   uK   [재시도 — 직전 응답의 frame_spatial_contract 가 다음 위반을u6    포함했습니다. 수정해서 재출력하세요:]r+   r,   r7   r/   Sz Shotz constraint[]r:   r1   z (got r{   r9   z (target_id=r5   z (count=z  - z: 
)rJ   rM   join)	fsc_violationslinesr   r]   r^   r`   rsnlocsuffixs	            rh   _format_retry_hintr     s    E UU=!eeL!ee$%eeHo"U3% ?\#a((C a<aj^1-FA#AkN#5Q7F\'
|1-FtC53%x01% & 99Urj   )rZ   List[Dict[str, Any]]returnr   )r_   Optional[Dict[str, Any]]rp   zOptional[List[str]]r   r   )r_   r   rp   z	List[str]r   r   )r   r~   r   r~   r   r   )r   rW   r2   r   r   r   )r   r   r   rW   )!__doc__
__future__r   loggingretypingr   r   r   r   app.core.errorsr   	getLoggerr   rm   r   __annotations__r   r   rQ   	frozensetrN   rS   rY   compilerT   rV   ri   rs   r   r   r   r   r   rj   rh   <module>r      so   #  	 , , $			8	$
 FGFTAUEGF
&" 
  )0('#      BC !"fg 2::m, bjj'K\3-&3-)3- 3-lMC&MCMC MC`
 6% 4rj   