
    j\                   *   d Z ddlmZ ddlZddlZddlZddlZddlmZm	Z	m
Z
mZ  ej                  e      ZdZ ed  ededz         D              ZdZdZ ej*                  d	      Z ej*                  d
      Z ej0                  d      ZdZdddddZh dZddhZdZdhZdZ ddddddddZ!d|dZ"efd d d d!	 	 	 	 	 	 	 	 	 d}d"Z#d~d#Z$dd$Z% ej0                  d%      Z&	 	 	 	 	 	 	 	 	 	 dd&Z'dd'Z(dd(Z)	 d	 	 	 	 	 	 	 dd)Z*d d d d*d+	 	 	 	 	 	 	 	 	 	 	 dd,Z+dd-Z,d.ddddd/	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dd0Z-	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dd1Z.d2Z/i d.d3d4d5dd6dd7d8d9d:d;d<d=d>d?d@dAdBdCdDdEdFdGdHdIdJdKdLdMdNdOZ0dhZ1h dPZ2h dQZ3h dRZ4h dSZ5h dTZ6h dUZ7h dVZ8dNhZ9ddWZ:ddXZ;dZ<dYZ=dZ>d dZ	 	 	 dd[Z?d dZ	 	 	 dd\Z@d dZdd]ZAd^ZBd_d`dadbZC eDeC       eDe      dchz
  k(  sJ dddZEddeZF	 	 	 	 	 	 ddfZGdgdh	 	 	 	 	 	 	 	 	 	 	 ddiZHddjZI	 d	 	 	 	 	 	 	 ddkZJddlZK	 	 	 	 	 	 ddmZLddnZMd d d do	 	 	 	 	 	 	 	 	 ddpZNddqZOdddrZPdsZQddtZRdddu	 	 	 	 	 	 	 	 	 	 	 	 	 ddvZSddwZTd|dxZUd*d d d4dy	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddzZVdd{ZWy)ut  레인1 마커 맵 — geometry 스키마·검증·결정론 합성 (설계 v2 Stage B).

LLM 은 normalized(0..1) geometry JSON 만 저작한다(합의 결정 2). 코드는
스키마 잠금·fail-closed 검증·PIL 결정론 합성만 담당 — base map PNG 는
immutable SOT(복사본에만 그림), 이미지 안에 고유명·장소명 0(슬롯 코드
E1../CAM 만).
    )annotationsN)AnyDictListOptional   c              #  &   K   | ]	  }d |   yw)EN ).0is     ]/Users/manta/Documents/Projects/TheRoad-I1/backend/app/modules/pipeline/outdoor_marker_map.py	<genexpr>r      s     D,CqQqc7,C      g{Gz?g      @g      f@z\b(?:E\d+|CAM)\boutdoor_marker_geometryz1.202607150100z2.202607241115z3.202607251321z4.202607270248)1234>   r   r   r   r   r   )toward_cameraaway_from_cameraprofileunspecified   )   2   r      )   r   r   r   )(   Z   r   r   )r    r!   r   <   g;On?gMbp?g      <@)entity_fillentity_outlinecamera_color	cone_fillentity_radius_fracline_width_fraccone_half_angle_degc                @    | t         vrt        d|        t         |    S )Nu4   outdoor_marker_geometry 프롬프트 버전 없음: )PROMPT_VERSION_MAP
ValueErrorversions    r   resolve_prompt_versionr/   ^   s*    ((OPWyYZZg&&    Finclude_anchorsinclude_viewinclude_facing_relationc                  ddddddddddddgd	d
}dt        d| dz         D cg c]  }d| 	 c}idddddddddddd}g d}||d}ddg}	|r%ddd|d<   |dgz   }ddd|d<   ddd|d<   |	ddgz   }	|r5||d<   ddd|d<   |ddgz   }||d<   ||d<   ddd|d<   ddd|d<   |	g dz   }	|r"dt        t              i|d<   d di|d!<   |dd!gz   }d||d	d
}
dd"|
d| d#d||	d	d
dd$dd%g d%d	d
S c c}w )&u=  normalized geometry 스키마 — slot enum·0..1 bounds 잠금.

    include_anchors (v2+, Codex ①): per-marker landmark anchor 를
    required 로 강제. False=기존 byte-identical.
    include_view (v3+, 2026-07-25 사용자 지적①): 시야 쐐기(camera
    view_left/view_right)와 피사체 facing 을 required 로 강제 —
    화각·향한 방향이 저작되지 않으면 마커 맵이 그릴 데이터가 없다.
    include_facing_relation (v4+): 카메라 대면 관계 선언 필드 2개를
    required 로 강제. False=기존 byte-identical.
    objectnumberr   r   typeminimummaximumxyr=   r>   Fr9   
propertiesrequiredadditionalPropertiesenumr
   stringr   )r9   	minLength)slot
subject_enr=   r>   )originlook_targetrH   rI      	anchor_enorigin_anchor_enlook_target_anchor_enfacingfacing_anchor_en	view_left
view_rightview_left_anchor_enview_right_anchor_en)rP   rQ   rR   rS   camera_facing_relationr9   camera_facing_evidencearray)r9   itemsminItemsmaxItems   )entity_placementscamerarationale_ko)rangelistCAMERA_FACING_RELATIONS)	max_slotsr2   r3   r4   pointr   placement_propsplacement_req	cam_propscam_req	placements              r   build_marker_geometry_schemarh   d   s    "qQ?"qQ?
 #J %E 5IM+BC+BaAaS'+BCD'a81;1;	O 5M+0 GI'G 19q'I$%519(J	$%1..	)*/1HII %*!1/.*+%3E(FF!&	+"'	,4<1+M	'(5=A,N	() 
 
  D0153015;X4F01%$&>)@ @ %! %	I   )9"
 !'#(-	 &.A>
 D %! _ Ds   C7c                T    t        | t        t        f      xr t        | t               S N)
isinstanceintfloatbool)vals    r   
_is_numberrp      s"    cC<(FC1F-FFr0   c           	         t        |t              s|j                  |  d       y dD ]F  }|j                  |      }t	        |      rd|cxk  rdk  r+n |j                  |  d| d|d       H y )Nu    가 객체 아님r<   r   r   .=u    숫자 범위(0..1) 밖)rk   dictappendgetrp   )labelpt
violationskro   s        r   _check_pointr{      so    b$UG#567ffQi#qC}1}q1SG3KLM r0   z\b[A-Z][0-9]+\b|\bCAM\bc                  ||r|j                  |  d       y t        |t              s*|j                  |  dt        |      j                          y |j                         }|s|j                  |  d       y t        d |D              r|j                  |  d       t        j                  |      r|j                  |  d|       y y )Nu    결손 (anchor 필수)u    이 문자열 아님: u    비어 있음c              3  8   K   | ]  }t        |      d k    yw)    N)ord)r   cs     r   r   z _check_anchor.<locals>.<genexpr>   s     
%13q6B;s   u    에 제어문자 포함uT    에 구조/legend 코드 토큰 포함 — 피처를 서술형으로 명명하라: )	ru   rk   strr9   __name__stripany_ANCHOR_CODE_TOKEN_REsearch)rw   rawry   rA   texts        r   _check_anchorr      s     {'>?@c3g,T#Y-?-?,@A	C99;DUG>23

%
%%UG#;<=##D)g !H&	' *r0   c                    t        j                  | dt         j                  z        }|t         j                   k  r|dt         j                  z  z  }|S |t         j                  kD  r|dt         j                  z  z  }|S )u>   (-pi, pi] 정규화 — 쐐기 포함 판정용 순수 기하.   )mathfmodpi)radouts     r   _norm_angler      sd    
))CTWW
%C
twwhq477{ J 
twwq477{Jr0   c                R    t        j                  |d   | d   z
  |d   | d   z
        S )Nr>   r=   )r   atan2)srcdsts     r   _headingr      s-    ::c#hS)3s8c#h+>??r0   c           
        	 | d   | d   | d   }}| d   }d|fd|ffD ]W  \  }}t        j                  |d   d   z
  |d   d   z
        }|t        k  s8|j                  d| d|d	d
t         d       Y t	        |      t	        |      }	t        |	z
        t              t        k  r3|j                  dt        j                  t                    dd       yt              t        kD  r3|j                  dt        j                  t                    dd       ydfd}
 |
|      s|j                  d       |xs g D ]E  }t        |t              s	  |
|      r	 |j                  |j                  d      xs d d       G y# t        t        f$ r Y Zw xY w# t        t        f$ r Y yw xY w)u  시야 쐐기 무결성 (v3) — 순수 기하. 좌표 결측/형식은 상위
    _check_point 이 이미 보고하므로 여기선 조용히 skip.

    placements 전달 시 **이 샷이 배치한 피사체가 전부 쐐기 안**인지도
    검사한다 (캔ary 실측: 카메라가 피사체 반대쪽을 보게 저작돼 마커
    맵이 빈 도로를 향한 쐐기를 그렸다 — 시야를 데이터화하기 전에는
    선 하나뿐이라 드러나지 않던 배치 오류).
    rH   rP   rQ   rI   r=   r>   zcamera.u9    이 origin 과 겹침 — 시야 경계 미성립 (dist=.4f < )u<   시야 쐐기가 열리지 않음 (좌우 경계 사이 각 z.1fuL   °) — 두 경계는 프레임의 좌/우 끝이라 서로 달라야 한다u   시야 쐐기가 과대 (u[   °) — 카메라가 사실상 전방향을 본다는 뜻이라 프레임이 성립 안 함c                ~    t        t        |       z
        }dk\  rd|cxk  xr k  S c S |cxk  xr dk  S c S )Nr   )r   r   )rx   rela_leftrH   spans     r   _insidez"_check_view_wedge.<locals>._inside  sS    !(62"6"?@*.!)Q#%%%#***r0   u|   look_target 이 시야 쐐기(view_left..view_right) 밖 — 카메라가 보는 지점은 프레임 안에 있어야 한다rF   u	   피사체u    가 시야 쐐기 밖 — 이 샷이 담는 피사체는 프레임 안에 있어야 한다. 카메라를 피사체 맞은편으로 옮기거나(origin), 시선/시야 경계를 피사체 쪽으로 다시 잡아라N)rx   Dict[str, Any]returnrn   )r   hypot_MIN_CAMERA_DISTru   r   r   abs_MIN_VIEW_SPAN_RADdegrees_MAX_VIEW_SPAN_RADrk   rt   KeyError	TypeErrorrv   )camry   
placementsleftrighttargetrw   rx   dista_rightr   plr   rH   r   s               @@@r   _check_view_wedger      s   1X+&L(9e]#&-e/DEIE2::bgs3RWvc{5JKD&&!!eW %!#Jc*:);1>? F &$'65)7V+,t9))N<<D	*3/ 0<<= Y+++DLLT,CC+H If fg 6?!!VW !&B&!"d+r{  # !!vvf~45 6; ; ' !),  i  sJ   AF0 BF0 &AF0 +;F0 'F/+F0 F-*F0 ,F--F0 0GG require_anchorsrequire_viewrequire_facing_relation	shot_textc          
        t        | t              sdgS g }| j                  d      }t        |t              s|j	                  d       g }|s|j	                  d       t        |      t        kD  r$|j	                  dt        |       dt                g }t        |      D ]2  \  }}	t        |	t              s|j	                  d| d       -|	j                  d	      xs d
}
|
t        vr |j	                  d| d|
dt         d       n|
|v r|j	                  d|
 d       |j	                  |
       |	j                  d      }|?t        |t              s/|j	                  |
xs | dt        |      j                          d
}|xs d
j                         }|s|j	                  |
xs | d       n/t        j                  |      r|j	                  |
xs | d|       t        |
xs | |	|       t!        |
xs | d|	j                  d      ||       t!        |
xs | d|	j                  d      ||       |	j                  d      }||s|j	                  |
xs | d       t        |
xs | d||       	 t#        j$                  |d   |	d   z
  |d   |	d   z
        }|t&        k  r|j	                  |
xs | d|dd       5 t-        d t        |      d z         D cg c]  }d!| 	 }}|r\t/        t1        |      t        z        |D cg c]  }|t        v s| c}k7  r'|j	                  d"t/        t1        |             d       | j                  d#      }t        |t              s|j	                  d$       i }t        d%|j                  d&      |       t        d'|j                  d(      |       t!        d)|j                  d*      ||       t!        d+|j                  d,      ||       t!        d-|j                  d.      ||       t!        d/|j                  d0      ||       |s"|j                  d1      |j                  d2      Et        d3|j                  d1      |       t        d4|j                  d2      |       t3        |||       	 t#        j$                  |d(   d   |d&   d   z
  |d(   d   |d&   d   z
        }|t4        k  r|j	                  d5|dd6t4         d       |st7        d7 |D              r|xs t9        |xs d
j                               }|r_t        |      D ]Q  \  }}	t        |	t              sd8| d9|	j                  d	      xs d: d}|j;                  t=        ||	|||;             S |S # t(        t*        f$ r Y w xY wc c}w c c}w # t(        t*        f$ r Y w xY w)<u  결정론 무결성 검증 — 위반 리스트 반환 (fail-closed 재시도용).

    스키마(call_structured strict)가 정상 경로를 막더라도, render 등
    public 소비자가 이 validator 를 직접 신뢰하므로 자체 완결이어야
    한다 (Codex Stage B N3): root/placement 타입, MAX_SLOTS 상한,
    slot enum, bool 좌표 배제까지 여기서 검증.

    require_anchors (v2+, Codex ①③): per-marker anchor(entity
    anchor_en, camera origin/look_target anchor)를 필수로 검증.
    False 여도 anchor 필드가 존재하면 형식(문자열·제어문자·구조 코드)
    은 검증한다 — 오염된 선택 필드 통과 차단.

    require_view (v3+): 시야 쐐기(camera view_left/view_right)와
    피사체 facing 을 필수로 검증. False 여도 존재하면 형식·기하
    (쐐기 개폐·look_target 포함·facing 자기겹침)는 검증한다.

    require_facing_relation (v4+): per-placement 카메라 대면 관계 선언과
    좌표의 정합 + 근거 인용 provenance 를 필수로 검증. False 여도 어느
    placement 든 camera_facing_relation 이 존재하면 enum·evidence 형식·
    unspecified 규칙·좌표 정합은 검증한다 — 오염된 선택 필드 통과 차단
    (위 두 게이트와 동일 관례).

    shot_text 는 그 샷의 SHOT TEXT 원문(인용 대조용). 인용 대조는
    require_facing_relation=True 이거나 shot_text 가 실제로 공급된
    경우에만 수행한다 — 둘 다 아니면(기본 플래그 소비자) 대조 자체가
    성립하지 않으므로 건너뛴다. require_facing_relation=True 인데
    shot_text 미공급이면 모든 명시 relation 이 거부된다(fail-closed).
    u   geometry 가 객체 아님r[   u#   entity_placements 가 배열 아님u   entity_placements 비어 있음zentity_placements u   개 — 최대 [u   ] placement 가 객체 아님rF   r   z] slot u    허용 밖 (E1..Er   zslot u    중복rG   u"    subject_en 이 문자열 아님: u    subject_en 비어 있음uR    subject_en 에 구조 토큰(E<n>/CAM) 포함 — 자연어 명사구만 허용: z
 anchor_enrK   )rA   z facing_anchor_enrO   rN   u    facing 결손 (v3 필수)z.facingr=   r>   uF    facing 이 자기 위치와 겹침 — 향한 방향 미성립 (dist=r   r   r
   u:   슬롯은 E1 부터 빠짐없이 순차여야 함 (현재 r\   u   camera 가 객체 아님zcamera.originrH   zcamera.look_targetrI   zcamera.origin_anchor_enrL   zcamera.look_target_anchor_enrM   zcamera.view_left_anchor_enrR   zcamera.view_right_anchor_enrS   rP   rQ   zcamera.view_leftzcamera.view_rightu>   camera 방향 벡터 미성립 — origin≈look_target (dist=r   c              3  f   K   | ])  }t        |t              xr |j                  d       du + yw)rT   N)rk   rt   rv   )r   r   s     r   r   z+validate_marker_geometry.<locals>.<genexpr>  s6      &B 	2tM(@!A!MMs   /1z
placement[z](?check_provenance)rk   rt   rv   r_   ru   len	MAX_SLOTS	enumerate_ALLOWED_SLOTSr   r9   r   r   _SUBJECT_STRUCT_TOKEN_REr   r{   r   r   r   _MIN_FACING_DISTr   r   r^   sortedsetr   r   r   rn   extend_check_facing_relation)geometryr   r   r   r   ry   r   seenr   r   rF   raw_subjectsubjectrN   fdistexpectedsr   r   _provenanceidxrw   s                         r   validate_marker_geometryr   :  s   B h%,--J12Jj$'?@
;<
:" Z 1L	ND:&2"d#!$ABCvvf~#~%A3gdX%7	{!DFT\dV734Dff\* ":k3+G91+?$--.0 K$"++-+DEF%,,W591+ 229> 		{R4yqk$bff[&9:$	& 	yqk*+RVV4F-G	/ !>!!TYQK/I"JKDIA;g.
C

3K"S')6#;C+@B++%%91+ &227A?@] 'f "'q#d)a-!89!8A!A3!8H9s4y>1278!1#6Ax78 8HPSTXPYIZH[[\]	_
,,x
 Cc4 45#''("3Z@%sww}'=zJ!377+=#>_. &0G(H_.
 $cgg.C&D\+ %sww/E'F\+ 	77;+77< ,')=zJ(#'',*?L#z:6
zzs#c(mC&88s#c(mC&88
 ""c
#&6%7q:; # && # . '_"##%2'
 $Z0R!"d+$SEBFF6N,Ac+B!D!!"8UI%0#2 3	 1 S i( 978R i  s8   AV0V2V7,V7*AV< V/.V/<WWc                   t        |      }|rt        ddj                  |             ddlm}m}m} |j                  t        j                  |             j                  d      }|j                  d|j                  d      }|j                  |      }|j                  \  }	}
t        |	|
      }t        dt!        |t"        d   z              }t        d	t!        |t"        d
   z              }|j%                         }|d   }|d   d   |	z  |d   d   |
z  }}|d   d   |	z  |d   d   |
z  }}t'        j(                  ||z
  ||z
        }t'        j*                  t"        d         }t'        j,                  |	|
      dz  }||t'        j.                  ||z
        z  z   ||t'        j0                  ||z
        z  z   f}||t'        j.                  ||z         z  z   ||t'        j0                  ||z         z  z   f}|j3                  ||f||gt"        d          |j5                  ||f||fgt"        d   |       |j7                  ||z
  ||z
  ||z   ||z   gt"        d   t"        d   t        d|d	z               |j9                  ||z   d	z   ||z
  fdt"        d   |       |d   D ]|  }|d   |	z  |d   |
z  }}|j7                  ||z
  ||z
  ||z   ||z   gt"        d   t"        d   t        d|d	z               |j9                  ||z   d	z   ||z
  f|d   t"        d   |       ~ |j;                  ||      }t        j                         }|j=                  |d       |j?                         S ) u  (DEPRECATED — 프로덕션 호출 금지) base map 위 마커 PIL 합성.

    2026-07-16 사용자 확정: 코드(PIL 등)로 이미지에 그리는 행위 절대 금지
    — 마커·콘·텍스트 오버레이 전부. 시각 요소는 항상 이미지 모델 몫.
    geometry 는 build_geometry_text_lines(ID-free 텍스트 직렬화)로만
    소비한다. 함수는 모듈 삭제 금지 원칙에 따라 보존만.
    invalid geometry: ; r   )Image	ImageDraw	ImageFontRGBA)r   r   r   r   r   r'   r   r(   r\   rH   r=   r>   rI   r)   g       @r&   )fillr%   )r   widthr$   r   )r   outliner   CAM)r   fontr[   r#   rF   PNG)format) r   r,   joinPILr   r   r   openioBytesIOconvertnewsizeDrawminmaxrl   _STYLEload_defaultr   r   radiansr   cossinpolygonlineellipser   alpha_compositesavegetvalue)base_pngr   ry   r   r   r   baseoverlaydrawwhunitrlwr   r   oxoytxtyanghalfray_lenp1p2r   exeyr   bufs                                 r   render_marker_mapr    s    *(3J-dii
.C-DEFF//::bjj*+33F;Dii		<8G>>'"D99DAqq!9DAs4&!56678A	QD6"3445	6B!!#D
8
C]3!#S]3%7!%;B$q(#m*<S*AA*EB
**R"Wb2g
&C<<456D
 jjA$G
w#*--
-
w#*--
-
/B
w#*--
-
w#*--
-
/BLL2r(B#&*=L>IIBx"b")?rIJLL	aaaa(N#V4D-E!R1Wo  
 	IIrAvz26"E.)  6 *+C1bgkB!VR!VR!VR!V,&7G0Haq/ 	 	

 			26A:rAv&6
m,4 	 	9 , 

g
.C
**,CHHSH<<>r0   r   )prompt_versioncall_structured_fnproject_configopik_metadatamax_attemptsc        
   	     0   ddl m}
 |ddlm} |}ddlm} ddlm}m} ddl	m
} t        |      } |t        d|	      } |t        d
|	      }|t        v }|t        v }|t        v }t!        |||      } ||      }|} ||       dj#                  d | j%                  dg       xs g D              |||dj'                         D ]  \  }}|j)                  d|z   dz   |      } ddd ||      d|dg}d}|}g }||	k  ro|dz  } |t        ||||t        |      }t+        |xs i ||||      } | s||dS | }dj#                  g d| D !cg c]  }!d|! 	 c}!z         }"|d|"dgz   }||	k  ro |
dd|	 ddj#                  |dd       z   d !      c c}!w )"u  샷 1개 geometry 저작 — 무결성 위반 시 위반 힌트 재시도.

    반환 {"geometry": <검증 통과 결과>, "attempts": n}.
    소진 시 AppError(step.contract_violation.outdoor_marker_geometry).

    v4+ 대면 관계: camera_facing_evidence 인용 대조 원문 = 프롬프트에
    실린 SHOT BLOCK **그대로**(build_shot_block — description + 등장
    인물 + 연출 카메라/배경 메모). LLM 이 본 텍스트와 대조 원문이
    갈라지면(예: description 만 대조) 카메라 메모 줄에서 인용한 근거가
    '원문에 없음'으로 거부돼 재시도가 소진된다 — fail-closed 계약이라
    미공급도 같은 결과다.
    r   )AppErrorNcall_structuredpng_part)build_legend_blockbuild_shot_blockload_promptsystemr-   user_templater1   
c              3  &   K   | ]	  }d |   yw)- Nr   )r   zs     r   r   z+run_marker_geometry_shot.<locals>.<genexpr>\  s      !
BbHBr   zone_labels_en)legend_blockzones_blocksegment_label
shot_blockscene_text_block{}r   z+TOP-DOWN SITE PLAN of the filming property:r9   r   r   r  schema_namer  r   )r   attempts)r   r   uK   [재시도 — 직전 응답이 아래 계약을 위반했습니다. 전부u4    고쳐서 전체 결과를 다시 출력하세요:]z  - z/step.contract_violation.outdoor_marker_geometryu(   marker geometry 계약 위반 (attempts=z): r   rJ   i  )codemessagestatus_code)app.core.errorsr  app.modules.llm.llm_clientr  %app.modules.pipeline.multiroll_geminir  +app.modules.pipeline.outdoor_shot_groundingr  r  app.modules.prompt_loaderr  r/   _MODULE_ANCHOR_GEOMETRY_PACKS_VIEW_GEOMETRY_PACKS_FACING_RELATION_GEOMETRY_PACKSrh   r   rv   rW   replacer   )#specshot
scene_textmap_pngsegment_label_enr  r  r  r  r  r  r  r  r  r  r  resolvedr  template	_anchored_viewed_relatedschemar&  filledkeyro   
base_partsr-  partslastresultry   vhints#                                      r   run_marker_geometry_shotrN  %  s&   2 )!>,> 6%n5H(H=F7OXFH"88I 44G!@@H)! (*F
 "$'JF*40yy !
"hh'7<BB!
 
 * &	 eg	S c	C5	 >	@(	J HED
\
!A#VUF)'	
 .Lb) ( "
  &H==yyE#-.:aaSz:./

 vt<==) \
!, >:<.L))D!H%&	 	 /s   F
c                V    ddl }|j                  | |||||t        |      |ddd      S )u  lane 콘티 사이드카 지문의 결정 입력 직렬화 (Codex Stage D HIGH-5).

    geometry(build_shot_block=vshot 전체)와 스케치(place/pose/carried/
    camera)가 소비하는 **모든** 결정 입력을 포함 — 부분 필드 추림 금지.
    r   N)r;  r<  r=  laner?  
place_textpose_clauses
carried_enTF)	sort_keysensure_ascii)jsondumpsr_   )	r;  vshotr=  rP  r?  rQ  rR  rS  _jsons	            r   lane_conti_fingerprint_payloadrZ    sF      ;;$ 0$ .$		
 U   r0   marker_map_sketchz1.202607150110r   z2.202607150300z3.202607152340z4.2026071622305z5.2026072410556z6.2026072411207z7.2026072411508z8.2026072508179z9.20260725132110z10.20260725134811z11.20260725135912z12.20260725184613z13.20260726021814z14.20260727034415z15.20260825125816z16.202608252310>   r   r\  r]  r^  r_  r`  ra  rb  rc  rd  re  rf  rg  >	   r_  r`  ra  rb  rc  rd  re  rf  rg  >   r`  ra  rb  rc  rd  re  rf  rg  >   r`  ra  rb  rc  rd  >   re  rf  rg  >   r\  r]  r^  r_  r`  ra  rb  rc  rd  re  rf  rg  >   r]  r^  r_  r`  ra  rb  rc  rd  re  rf  rg  c                    | t         v S )uU   이 팩에서 좌표가 권위인가 — 표기 갈래를 정하는 유일한 자리.)_COORD_AUTHORITY_PACKSr  s    r   _coord_firstrk  i  s    333r0   c                    t        |       ryy)uD  마커 검사 user 턴 머리말 — 무엇이 권위인지 **한 번** 말한다.

    ★스텝이 이 문장을 손으로 들고 있었고, 팩을 v16 으로 올려도 머리말은
     "fractions are coarse estimates" 라고 옛말을 하고 있었다. 팩 버전과
     함께 움직이도록 여기로 옮긴다.
    u   Expected staging — the coordinates are the authority (x runs left→right 0..1, y runs top→bottom 0..1 of the canvas); a named landmark is only a pointer to the same spot:u   Expected staging (each marker described relative to landmarks visible on the plan; fractions are coarse estimates, x left→right 0..1, y top→bottom 0..1):)rk  rj  s    r   marker_check_user_headerrm  n  s     N#Dr0   	1024x1024coord_firstc               ^    |rt        | |      S d| d   dd| d   d}|r| d| dS d| dS )	uB  쐐기 경계·facing 이 가리키는 한 점.

    ★2026-08-25 방향 수리 — 이 한 줄이 **두 프롬프트에 같이 쓰인다.**
     두 프롬프트에서 권위가 서로 다르므로 표기도 갈라야 한다:

       마커 맵 (`coord_first=True`)  좌표가 권위. 도면 위에 동그라미를
         찍는 일이고, 그 도면에는 나침반이 없다. "past the west edge
         (coarse estimate x=0.20, y=0.30)" 라고 쓰면 그림에서 짚을 수
         없는 낱말이 짚을 수 있는 값을 이긴다 — 부채꼴이 세 번 다
         반대편으로 열린 자리가 바로 여기다(S2sh1).
       콘티 스케치 (기본)  **마커 맵 그림이 권위**다. 좌표는 감사용
         병기이고, staging head 가 "neither ever overrides the marked
         plan" 이라고 못박아 두었다. 여기까지 좌표를 권위로 바꾸면 그
         문장과 정면으로 부딪히고, 위에서 본 좌표를 화면 좌우로 읽는
         옛 반전 결함(v11)을 되살린다. 그래서 **기본값은 그대로 둔다.**
    zx=r=   .2f, y=r>   z (coarse estimate r   ()_at_xy)rx   anchorrp  coarses       r   _place_textrx    sZ    $ b&!!"S'#d2c73-0F286($VHA.]r0   c                  | j                  d      | j                  d      }}t        |t              rt        |t              sy	 t        |t	        | j                  d      xs d      j                         |      }t        |t	        | j                  d      xs d      j                         |      }d| d| S # t        t        f$ r Y yw xY w)	uX   시야 쐐기 서술 — geometry v3 데이터 부재 시 빈 문자열(구 팩 불변).rP   rQ   r   rR   ro  rS   z#the frame's LEFT edge cuts through z and its RIGHT edge through rv   rk   rt   rx  r   r   r   r   )r   rp  vlvrr   r   s         r   _view_wedge_textr}    s     WW[!377<#8Bb$z"d';CGG128b9??A#% CGG239r:@@B#% .dV 4'	 i  s   A0B= =CCc          	         | j                  d      }t        |t              sy	 dt        |t	        | j                  d      xs d      j                         |      z   S # t        t        f$ r Y yw xY w)uI   피사체가 향한 쪽 서술 — v3 데이터 부재 시 빈 문자열.rN   r   zfacing rO   ro  rz  )r   rp  fs      r   _facing_textr    st    
xAa;s266,-34::<#% % 	% i  s   :A A10A1g      ?]away from the camera, into the depth of the shot, so the camera sees this subject from behindAtoward the camera, so the camera sees this subject from the frontz9side-on to the camera, neither toward it nor away from itr   r   r   r   c                    	 | d   d   | d   d   }}| d   d   |z
  }| d   d   |z
  }t        j                  ||      }|dk  ry||z  ||z  f}||d    |d   ffS # t         t        f$ r Y yw xY w)	u   (forward, right) 단위벡터 — 맵은 y 가 아래로 증가하므로 카메라가
    북(위)을 보면 forward=(0,-1), right=(+1,0)=동쪽=화면 오른쪽.rH   r=   r>   rI   N&.>r   r   )r   r   r   r   )r   r   r  dxdynormfwds          r   _screen_basisr    s    Xs#S]3%7B$r)$r) ::b"Dd{9b4i
 C#a&#a&!!! i  s   &A A-,A-c                   t        |       }|y|\  }}	 |d   | d   d   z
  |d   | d   d   z
  f}|d   |d   z  |d   |d   z  z   }|dk  ry|d   |d   z  |d   |d   z  z   |z  }|dkD  ry|d	k  ry
y# t        t        f$ r Y yw xY w)uB   피사체가 프레임의 어느 쪽에 서는지 (순수 투영).r   r=   rH   r>   r   r   g
ףp=
?zin the RIGHT part of the frameg
ףp=
ǿzin the LEFT part of the frameznear the CENTER of the frame)r  r   r   )r   r   basisr  r   r   depthlaterals           r   _screen_side_textr    s    #E}JC#wXs++RWs8}S7I-IJ FSVOc!fs1vo-Ez1va 3q6E!H#44=G~/.) i  s   A5 5BBc                4   t        |       }|t        |j                  d      t              sy|\  }}	 |d   d   |d   z
  |d   d   |d   z
  f}t        j                  | }|dk  ry|d   |z  |d   z  |d   |z  |d   z  z   S # t        t
        f$ r Y yw xY w)u  피사체 facing 의 카메라 축 성분 (-1..+1).

    양수 = 카메라에서 멀어지는 쪽(카메라는 등을 본다),
    음수 = 카메라 쪽으로 도는 쪽(카메라는 앞을 본다),
    0 근처 = 카메라 축에 직각(측면).

    좌표가 없거나 facing 이 자기 위치와 같으면 None — 순수 벡터 연산이며
    의미 판단은 하지 않는다.
    NrN   r=   r>   r  r   r   )r  rk   rv   rt   r   r   r   r   )r   r   r  r  _rightvecr  s          r   facing_camera_depthr  +  s     #E}Jrvvh'7>KC(|C 2c7*BxL,=3,GH ::sDd{FTMSV#s1v}A&>>> i  s   B BBTr   c               l   ddl m} |j                  d      }|t        vr| d|gS |j                  d      }t	        |t
              s| dgS  ||      }g }	|dk(  r|r|	j                  | d       |	S |r>|s|	j                  | d	| d
       n$| ||xs d      vr|	j                  | d|       t        | |      }
|
|	j                  | d       |	S |
t        kD  |
t         k  t        |
      t        k  d|   }|s0|	j                  | d	| d|
ddt         dt         dt         d       |	S )u  선언된 대면 관계 ↔ 좌표 정합 + 근거 provenance (v4).

    구간은 하나의 named epsilon(_SCREEN_EPS)으로 **완전 비중첩**이다:
      away_from_camera : dep > +eps
      toward_camera    : dep < -eps
      profile          : abs(dep) <= eps
    겹치게 정의하면(예: away 를 dep>0 으로) eps=0.25·dep=0.1 이 away 와
    profile 양쪽을 통과해 보증이 무너진다.

    evidence 는 필드 존재로 통과시키지 않는다 — 명시 relation 이면 SHOT
    TEXT 원문에 실재하는 인용이어야 하고, unspecified 면 비어 있어야
    한다. substring 으로 의미를 판정하는 것이 아니라 LLM 이 제시한 인용의
    진위만 확인하는 결정론 게이트다(outdoor_lane_plan._check_evidence 와
    동형).

    check_provenance=False (리뷰 지적 Important-2): 인용 대조 **만**
    건너뛴다 — enum·evidence 타입·unspecified 규칙·좌표 정합은 항상
    검증한다. 저작 경로가 아닌 소비자(build_geometry_text_lines 등)는
    SHOT TEXT 를 갖고 있지 않아 대조 자체가 성립하지 않는데, 그 이유로
    좌표 정합 검사까지 통째로 꺼 두면 오염 relation 이 그대로 통과한다.
    r   )ws_normalizerT   u/    camera_facing_relation 이 계약 값 아님: rU   u,    camera_facing_evidence 가 문자열 아님r   u    camera_facing_relation=unspecified 인데 camera_facing_evidence 가 비어 있지 않음 — 근거만 있고 선언이 없는 상태 금지z camera_facing_relation=u>    인데 camera_facing_evidence(SHOT TEXT 원문 인용) 누락r   ua    camera_facing_evidence 인용이 SHOT TEXT 원문에 없음 — 원문 그대로 인용할 것: uq    facing 으로 카메라 축 성분을 계산할 수 없음 (facing/좌표 결손 또는 자기 위치와 동일)r  u1    인데 facing 좌표의 카메라 축 성분이 z.3fu,    — 관계를 만족하지 않는다 (away>z
, toward<-z, profile |dep|<=uD   ). SHOT TEXT 가 말한 대면 관계대로 facing 점을 옮길 것)
&app.modules.pipeline.outdoor_lane_planr  rv   r`   rk   r   ru   r  _SCREEN_EPSr   )r   r   rw   r   r   r  r   ev_rawevr   depoks               r   r   r   E  s   2 D
&&)
*C
))'HPQQVV,-Ffc"'EFGG	f	BC
mJJ' 1 1
 
JJ'1# 7I I |IO44JJ' 779f> c2
&C
{

g D D	
 
+-|+s8{*
 
	
B
 

g-cU 3''*3i 0 MK= 9)] +44	
 Jr0   c                N   t        |       }|t        |j                  d      t              sy|\  }}	 |d   d   |d   z
  |d   d   |d   z
  f}t        j                  | }|dk  ry|d   |z  |d   |z  f}|d   |d   z  |d   |d   z  z   }|d   |d   z  |d   |d   z  z   }|t        kD  rdn|t         k  rd	nd}	|j                  d
      }
|
t        v r
t        |
   }n|t        kD  rd}n|t         k  rd}nd}|	r	|r|	 d| S |	xs |xs dS # t        t
        f$ r Y yw xY w)u  피사체가 향한 쪽을 **화면 기준**으로.

    좌/우는 언제나 순수 투영이고, 카메라 대면(앞/뒤/측면)만 선언
    relation 이 있으면 그 값에서 렌더한다 — 여기 도달한 선언값은
    _check_facing_relation 이 좌표와 정합을 확인한 뒤이므로 eps 추론보다
    권위다. 코드가 의미를 새로 판정하는 것이 아니라 검증 통과한 선언을
    문장으로 옮길 뿐이다.
    rN   r   r=   r>   r  r   r   ztoward the RIGHT of the frameztoward the LEFT of the framerT   r  r  z and zacross the frame)
r  rk   rv   rt   r   r   r   r   r  _FACING_RELATION_PHRASES)r   r   r  r  r   r  r  latr  horizr   r  s               r   _facing_screen_textr    s    #E}Jrvvh'7>JC(|C 2c7*BxL,=3,GH ::sDd{q6D=#a&4-
(C
a&58
c!fuQx/
/C
a&3q6/CFSVO
+C+.+<'/2k\/A+ 
 &&)
*C &&
 )-	{	3 	 
|	 	
 eW%%/E///S i  s   D D$#D$c                f   |j                  d      }t        |t              sy| xs g D ]q  }||u st        |t              s	 t        j                  |d   |d   z
  |d   |d   z
        |k  r.t        |j                  d      xs d      j                         c S s y# t        t        f$ r Y w xY w)u   facing 좌표가 다른 피사체의 위치와 사실상 같은 점이면 그 피사체를
    보는 것 — 좌표 근접 판정(의미 판단 아님).rN   r   r=   r>   rG   )	rv   rk   rt   r   r   r   r   r   r   )r   r   tolfaceothers        r   _facing_subject_enr    s    
 66(DdD!!r!B;j5	zzc
T#Y&c
T#Y(> 599\28b9??AA	 "  )$ 		s   ABB0/B0c                   t        |       }|rt        ddj                  |             | d   }|d   d   |d   d   }}|d   d   |d   d   }}t        |j	                  d      xs d	      j                         }t        |j	                  d
      xs d	      j                         }t        |      }	|	rd|	 dnd}
|r|rd| d|dd|dd| d|dd|dd|
 }nd|dd|dd|dd|dd|
 
}|g}| d   }|D ]  }t        |j	                  d      xs d	      j                         }t        |      }|rd| nd	}|rBt        ||      }|r|d| z  }t        ||      }|rt        ||      }|d| |rd| nd	z   z  }|r,|j                  d|d    d| d|d   dd|d   dd| 
       |j                  d|d    d|d   dd|d   dd|         |S )u  geometry JSON → ID-free planning prose 라인 (R5 — 순수 직렬화).

    슬롯 코드(E1..)/CAM/marker/wedge 토큰을 이미지 모델에 노출하지
    않는다 — 금지 대상을 프라이밍하지 않기 위함(Codex HIGH-5, validator
    가 subject_en 의 구조 토큰을 reject 해 봉인). 좌표 표기는
    normalized (x=?, y=?) 소수 2자리. 코드는 의미 판단 없이 데이터
    직렬화만 수행한다.
    r   r   r\   rH   r=   r>   rI   rL   r   rM   uZ    — everything between those two edges is in frame, anything outside them is out of framezI; subjects outside the camera's horizontal field of view are out of framez- the camera stands z (coarse estimate x=rr  rs  z'); its sight line ends at LOOK TARGET: r   z- the camera stands at (x=z) and looks toward (x=r[   rK   , u%    — from this camera the subject is z and faces z$; this subject is turned toward the r   rG   z stands z stands at (x=)r   r,   r   r   rv   r   r}  r  r  r  r  ru   )r   ry   r   r   r  r  r  o_anchort_anchorwedgefov_tailcam_linelinesr   r   rv  rN   tailscreen_sidescreen_facelooks_ats                        r   build_geometry_text_linesr    s    *(3J-dii
.C-DEFF
8
C]3Xs!3B$c-&8&=B377-.4"5;;=H377239r:@@BH S!E  UG 0 	0		  H"8*,@C IC?z J""$SbXQxjB 	 )CRH =CRHAhZ9 	 JE-.JRVVK(.B/557b! &F8}B
 
 ,C4K?}MM-c26K-j"= !+/" >hZH(*,
 LLR%&hvh 7&&(gc]$r#wsm1TFL
 LLR%& 'gc]$r#wsm1TF<O V Lr0   c           
         ddl m} t        |       }|rt        ddj	                  |             |t
        vrt        d| d      t        |      }|t        v }|t        v }|r-t        | d|      }|rt        d	dj	                  |      z         | d
   }t        |      }	|rdt        |d   t        |j                  d      xs d      |	      z   dz   t        |d   t        |j                  d      xs d      |	      z   }
|r|
dt        ||	      z   z  }
|
g}| d   D ]x  }|rd|d    d|d    dnd|d    d}|t        |d   |d   dt        |j                  d      xs d      |	      z   }|r|dt        ||	      z   z  }|j                  |       z n\d|d   d   d d!|d   d   d d"|d   d   d d!|d   d   d d#	g}| d   D ](  }|j                  d|d    d$|d   d d!|d   d d#       *  |t         d%|&      j#                         d'd(j	                  |      z   g}|sBt        | j                  d)      xs d      j#                         }|r|j                  d*|z          d+j	                  |      S ),u  i2i 마커 작화 프롬프트 (v5+) — 참조=클린 canon 맵 1장.

    2026-07-24 사용자 지시: '맵 위에 카메라 위치+엔티티 표시' 단계는
    필수이며 구현은 이미지 모델 i2i — 코드(PIL) 드로잉 금지의 해법은
    단계 제거가 아니다. 이 프롬프트만 슬롯 라벨(CAM/E1..)을 노출한다:
    작화 대상이 마커 그 자체이기 때문. 스케치 경로의 ID-free 직렬화
    (build_geometry_text_lines)와 의도적으로 분리.
    r   r  r   r   marker_map_sketch vuL    팩에는 marker_annotate 계약이 없음 — i2i 마커 맵은 v5+ 전용Tr   r   u(   anchored annotate 요구 계약 위반: r\   z- CAM (camera): circle rH   rL   r   ro  &; its sight line ends at LOOK TARGET: rI   rM   r[   r   rF   z (rG   z
): circle z	: circle r=   r>   r<   rK   r  z- CAM (camera): circle at (x=rr  rs  ), sight line toward (x=r   z: circle at (x=marker_annotater-   zMARKERS TO DRAW:
r  r]   u   PLACEMENT INTENT (authoritative cross-check, Korean — the coordinates above and this intent describe the same staging; if a marker seems to land elsewhere, re-measure the fractions): 

)r5  r  r   r,   r   _ANNOTATE_PACKSresolve_sketch_pack_version_ANCHOR_SKETCH_PACKS_VIEW_SKETCH_PACKSrk  rx  r   rv   r}  r  ru   SKETCH_PACK_MODULEr   )r   r  r  ry   r@  anchoredviewedanchor_violationsr   rp  r  r  r   headr   rI  intents                    r   build_marker_annotate_promptr  8  sL    6)(3J-dii
.C-DEFF_,!.!1 2@ @A 	A +>:H!55H11F 5dA:))-./0 0 8
C ~.K%#h-cgg&89?R@&133 7	7
 #m,cgg&=>D"E&133 	 /MMMH
./B
  RZL2l#3"4J?!#BvJ<y9  +gBsG,BFF;'-2.KI ID |BKHHHLL 0 (mC(-T#h-2DS1I J-(-c2 3',S1	4
 ./BLLRZL !gc]$r#wsm16 0 	&(9$	&&+egtyy//E
 X\\.17R8>>@LL# &,,-
 ;;ur0   c                    ddl m} | t        vrt        d|  d      t	        |       } |t
        d|      j                         S )u?   마커 맵 VLM 검증 system (v5+) — 판정 호출은 caller.r   r  r  uI    팩에는 marker_check 계약이 없음 — i2i 마커 맵은 v5+ 전용marker_check_judger-   )r5  r  r  r,   r  r  r   )r  r  r@  s      r   load_marker_check_sysr    sZ    5_,!.!1 2@ @A 	A +>:H0(DDIEGLr0   r   r   rp  c               Z   t        | ||      }|rt        ddj                  |             | d   }t        |j	                  d      xs d      j                         }t        |j	                  d      xs d      j                         }t        ||      }|rd	| nd}	|r0|r.d
t        |d   ||      z   dz   t        |d   ||      z   |	z   g}
n.d|d   d   dd|d   d   dd|d   d   dd|d   d   dd|	 
g}
| d   D ]  }t        |j	                  d      xs d      j                         }t        ||      }|rd| nd}|r4|
j                  d|d    dt        |d   |d   d||      z   |z          {|
j                  d|d    d|d   dd|d   dd|         |
S )u"  마커 검증 user 턴의 기대 배치 직렬화 (annotate 와 동일 표기).

    require_anchors (Codex 재리뷰 HIGH-3): anchor 팩 소비자는 True 로
    호출 — partial/missing anchor 가 수치 경로로 조용히 하강하지 않고
    ValueError (public builder 재검증 계약의 독립 성립).
    require_view (v3+): 쐐기·facing 팩 소비자는 True — 같은 이유로
    부분 결손이 구 표기로 하강하지 않는다.

    ★2026-08-25 방향 수리 — 종전에는 **검사기 문안만** anchor 산문을 앞에
     세우고 좌표를 "coarse estimate" 로 뒤에 달았다. 그리는 쪽은 좌표가
     권위인데 재는 쪽은 산문이 권위여서, 기하가 중앙에 둔 피사체를 검사기가
     "남동에 있어야 하는데 아니다" 로 반려하는 일이 실제로 났다. 지도에는
     나침반이 없으므로 「남동」은 그림 위에서 확인할 수 없는 말이다.
     그래서 그리는 쪽이 쓰는 표기(`_at_xy`)를 **그대로** 쓴다 — 좌표가
     권위, 화면 구역이 그 좌표를 눈으로 짚는 말, anchor 는 참고.
     ★anchor 를 빼지는 않는다. 나침반이 없는 도면에서 자리를 짚어 주는
      유일한 재료다(빼면 좌표만 남아 그림 위에서 못 찾는다).
    r  r   r   r\   rL   r   rM   ro  z; its view wedge is such that z- CAM expected rH   r  rI   z- CAM expected at (x=r=   rr  rs  r>   r  r   r[   rK   r  r   rF   z
 expected r<   z expected at (x=)
r   r,   r   r   rv   r   r}  rx  r  ru   )r   r   r   rp  ry   r   r  r  r  
wedge_tailr  r   rv  rN   r  s                  r   build_marker_check_user_linesr    sD   , */!#J -dii
.C-DEFF
8
C377-.4"5;;=H377239r:@@BHSk:E=B1%9JH "c(mX*577 ;; c-0(*57	7 

(mC(-T#h-2DS1I J-(-c2 3',S1:,	@
 *+RVVK(.B/557bk: &F8}BLLRZL
+BsG"S':F*577  LLRZL !gc]$r#wsm1TF< ,  Lr0   c                    |dk  rdn|dkD  rdnd}| dk  rdn| dkD  rdnd}|dk(  r|dk(  ry	|dk(  rd
| dS |dk(  rd
| dS d
| d| dS )u>  좌표를 **그림에서 어디인가**로 옮긴다 — 방위가 아니라 화면 위치.

    ★2026-08-25 실측: 이 도면에는 방위 표시가 없다(나침반도, "위가 북쪽"도
     없다). 그런데 문안은 "southeast" 를 계속 불렀다 — 모델은 그 말을 그림에
     앉힐 기준이 없어 **스스로 짐작**했고, 여섯 번 다 같은 짐작을 했다.
     좌표는 처음부터 정확했으므로 그것을 그림 좌표 그대로 말한다.
    ★수치를 말로 바꾸는 것이지 글자의 뜻을 재는 것이 아니다.
    g(\?uppergQ?lowermiddler   r   centerzthe center of the planzthe z edge area of the planz middle of the plan-z area of the planr   )r=   r>   verhors       r   _screen_zone_enr    s     X'q4xGXCH&a$h7HC
h3(?'
hcU011
hcU-..#au-..r0   c           	         t        | d         }t        | d         }d|dd|ddt        ||       }|j                         r|d|j                          dz  }|S )	u  마커 한 자리 — **좌표가 권위**이고 랜드마크 서술은 참고다.

    종전에는 랜드마크 서술(방위 낱말 포함)이 앞에 오고 좌표가 "coarse
    estimate" 라는 이름으로 괄호에 들어갔다. 「대략적인 추정」이라 불린 값이
    실제로는 유일하게 정확한 값이었고, 앞의 방위 낱말이 그것을 이겼다.
    순서를 뒤집고 이름을 바로잡는다.

    ★축 관례와 「좌표가 권위」는 여기서 되풀이하지 않는다 — 팩 머리말
     (marker_annotate·marker_check_judge v16)과 고르기 문안이 **한 번씩**
     말한다. 마커 한 자리마다 붙이면 한 프롬프트에 예닐곱 번 실린다.
    r=   r>   zat x=rr  rs      — z (landmark, for reference: r   )rm   r  r   )rb   rK   r=   r>   r   s        r   ru  ru    sq     	eCjAeCjA!CQsG5A)>(?
@C 	,Y__->,?qAAJr0   )camera_positioncamera_wedgeentity_positionentity_facingplan_preservedextra_contentc                 b    ddddiddiddddt        t              dddidd	d
gdddg ddS )u  마커 맵 검증 스키마 — 존재·근사 위치·플랜 보존 + **어긋난 자리 목록**.

    `violations` 는 「가장 좋은 것」을 고르는 유일한 재료다. 비어 있으면
    통과이고, 여러 장 중에서는 **적게 어긋난 것**이 낫다.
    r6   Fr9   booleanrD   rV   )r9   rC   )axisnote_kor  r  r9   rB   r@   rA   )r9   rW   )
markers_ok
details_kory   )r_   MARKER_VIOLATION_AXESr   r0   r   build_marker_check_schemar  *  sj      %!9-!8,$,1)1)-.C)D!F$*H#5#
 "( 3	
" ?) r0   )r  r  c           	        t        |      dk  ryddlm} ddlm} t        |      g}|t        ||t        v |t        v t        |            z  }|j                  d       |j                  d       dd	j                  |      d
ddd
 ||       g}	t        |      D ]e  \  }
}t        |j                  d      xs i       }d|
dz    d}|r	|d| dz  }|	j                  d|d
       |	j                   ||d                g ddddt        |      dddidddgd}	  |dt        |      |	||d|      }t'        |j                  d      xs d      dz
  }d|cxk  rt        |      k  sy yt)        |j                  d      xs d      ||   d <   |S # t         $ r }t"        j%                  d|       Y d}~yd}~ww xY w)!u  세 장 중 **어느 것이 기하를 가장 잘 지켰나** — VLM 이 고른다.

    2026-08-25 사용자 지시: "nb2 가 3번 실패하면 그냥 그 셋 중에 하나 하라고,
    VLM 으로". 통과가 없다고 멈추지 않고 **가장 나은 판으로 계속 간다**.

    ★「몇 점인가」를 묻지 않는다. 절대 점수는 못 믿는다는 실측이 있고
     (Qwen 판정 실험: 관찰·셈은 좋고 matches_brief 는 못 믿는다), 여기서
     필요한 것도 순위 하나뿐이다 — **여럿 중 어느 것**만 묻는다.
    ★판정 계약은 검사기가 쓰던 것을 그대로 쓴다. 「무엇이 옳은가」를 두 벌로
     두면 고르는 기준과 재는 기준이 갈린다.
    ★후보가 하나뿐이면 부르지 않는다 — 물어볼 것이 없는 데 돈을 쓰지 않는다.

    반환: 고른 후보의 인덱스. 판정이 못 돌면 0(첫 판)을 돌려준다 —
    판정기가 죽었다고 이미 산 그림을 다 버리면 안 된다.
    r   r   r  r  r  r   u   None of the candidates below passed the check. Choose the ONE that departs least from the staging above — judge only what you can see, and do not rate them on a scale. If two are equally close, choose the earlier one.r   r  r*  zORIGINAL CLEAN SITE PLAN:checkz
CANDIDATE :u(    (the checker rejected this one for — r   pngr6   Fintegerr8   r9   rD   )choicewhy_kor  r  r  lane_marker_map_pickr+  u6   marker map 고르기 실패 — 첫 판을 쓴다 (%r)Npick_why_ko)r   r2  r  r3  r  rm  r  r  r  rk  ru   r   r   marker_reject_linerv   r  	Exceptionloggerwarningrl   r   )r   
candidatesr   r  r  r  r  r  r  rI  r   candwhyr  rE  r   excr   s                     r   pick_best_marker_mapr  H  s   0 :!:>%n56E	*'+??$(:: 0	2 2E
 
LL	LL	 5!12!<=#E
 Z(4 !'!2!8b9AE7!$>se1EEDfd34Xd5k*+ )  %(Q"%j/3x(

 x(	F"$9.$I6..'	) cggh$1
%
)C%c*o% &%():)@b%AJsOM"J  OQTUs    F 	F?F::F?c                   t        | t              sy| j                  d      xs g D cg c]0  }t        |t              rt        |j                  d      xs d      2 }}|D cg c]  }|s|	 }}t        | j                  d      xs d      j	                         }|r|rdj                  |       d| S dj                  |      xs |S c c}w c c}w )u<  이 판이 무엇 때문에 반려됐나 — 고를 때 함께 보여 줄 한 줄.

    ★셈으로 순위를 매기지 않는다(사용자 지시: 고르는 것은 VLM). 여기서
     만드는 것은 **판정기가 남긴 사유**뿐이고, 어느 것이 나은지는 그림을
     보는 쪽이 정한다.
    r   ry   r  r  r  r  )rk   rt   rv   r   r   r   )r  rL  axesadetails        r   r  r    s     eT".3ii.E.K.K $.K!T" f#$.KD $!t!qAtD!<(.B/557F))D/"%x0099T?$f$$!s   5C$C,Cc                @    | t         vrt        d|        t         |    S )Nu%   marker_map_sketch 팩 버전 없음: )SKETCH_PACK_VERSION_MAPr,   r-   s    r   r  r    s)    --@	JKK"7++r0   )camera_direction_enseed_look_attachedplate_attachedr  c        	           ddl m}	 t        ||t        v |t        v       }
|
rt        ddj                  |
             |r|t        vrt        d| d      |r|t        vrt        d| d      t        |      }|t        v rd	nd
}|t        v r |	t        d|      j                          |	t        ||      j                          |	t        d|      j                         dz   dj                  t        |            z   g}|r,|j                   |	t        d|      j                                |r|j                  d|z          |j                  d|        |j                  d|         |r |j                  dj                  |             |r|j                  d|z          |t         v r,|j                   |	t        d|      j                                |j                   |	t        d|      j                                |j                   |	t        d|      j                                dj                  |      S |j#                  d      xs g D cg c]  }|d    d|d     }} |	t        d|      j                          |	t        ||      j                         ddj                  |      z   g}|r,|j                   |	t        d|      j                                |r|j                  d|z          |j                  d|        |j                  d|         |r |j                  dj                  |             |r|j                  d|z          |j                   |	t        d|      j                                |j                   |	t        d|      j                                dj                  |      S c c}w )u  스케치 프롬프트 조립. 소비 전 geometry 무결성 재검증 (fail-closed
    — persisted/reloaded geometry 도 신뢰하지 않음).

    v4+ (_GEOMETRY_TEXT_PACKS): 참조=클린 SITE PLAN — head → STYLE
    → STAGING GEOMETRY(ID-free 텍스트 직렬화) → CAMERA DIRECTION →
    LOCATION → SHOT TEXT → pose → carried → no_marker → no_text.
    v1~v3 (마커 굽힌 맵 전제, deprecated 경로): head → STYLE →
    slot 매핑 → [SEED LOOK] → 이하 동일.

    STYLE 스템은 팩별로 갈린다 — v14+(_MANNEQUIN_SKETCH_PACKS)=
    mannequin_frame(인물=목각 인형), 그 외=light_frame.

    seed_look_attached=True (레인2, v3 팩 전용): 2번째 참조=구조물 확정
    실사(형태 SOT) 조항을 조립. seed_look stem 이 없는 팩(v1/v2/v4)에서
    True 요청 = ValueError (조항 없이 참조만 첨부되는 silent 계약 위반
    차단).r   r  r  r   r   r  uO    팩에는 seed_look 계약이 없음 — seed_look_attached=True 는 v3 전용ur    팩에는 plate_look 계약이 없음 — 조항 없이 배경 참조만 첨부되는 silent 계약 위반 차단mannequin_framelight_framesketch_headr-   staging_geometry_headr  
plate_lookz=CAMERA DIRECTION (vertical angle / elevation / framing SOT): zTHE LOCATION: z#SHOT TEXT (authoritative, Korean): z!CARRIED STATE (persist exactly): naturalism_clause	no_markerno_textr  r[   rF   z = rG   zMAP SLOT MEANINGS:
	seed_look)r5  r  r   r  r  r,   r   _SEED_LOOK_PACKS_PLATE_LOOK_PACKSr  _MANNEQUIN_SKETCH_PACKS_GEOMETRY_TEXT_PACKSr  r   r  ru   _NATURALISM_SKETCH_PACKSrv   )	shot_descrQ  r   rR  rS  r  r  r   r  r  ry   r@  _style_stemrI  r   
slot_liness                   r   build_marker_sketch_promptr    s   8 6)'+??$(::=J -dii
.C-DEFFn4DD!.!1 2? ?
 	
 .0AA!.!1 2a a
 	

 +>:H 44 	 
 --*M (**/%'*K (**/%'.0G$,..3eg#<X#FGH

  LL.$,..3eg LL./ 	~j\23:9+FGLL<01LL<zIJ55 LL.0C$,..3eg 	*K (**/%'	
 	*I (**/%'	
 {{5!! ,,239r99B f:,c"\*+,9  
 	&$	&&+eg&$	&&+eg:!66E *K (**/%'	
 K!"	
 
LL>*./	LL6ykBCTYY|,-8:EF	LL&XFLLN 
LL&	8DJJL ;;uCs   Oc                 "    dddiddidddgdd	S )
uF   스케치 마커 leakage VLM gate 스키마 (판정 호출은 caller).r6   r9   r  rD   )has_marker_leakager  r  r  Fr?   r   r   r0   r   build_leakage_schemar  >  s3     #)9"5!8,
 *<8 % r0   )r.   r   r   r   )
ra   rl   r2   rn   r3   rn   r4   rn   r   r   )ro   r   r   rn   )rw   r   rx   r   ry   	List[str]r   None)
rw   r   r   r   ry   r  rA   rn   r   r  )r   rm   r   rm   )r   r   r   r   r   rm   rj   )r   r   ry   r  r   r   r   r  )r   r   r   rn   r   rn   r   rn   r   r   r   r  )r   bytesr   r   r   r  )r;  r   r<  r   r=  r   r>  r  r?  r   r  r   r  Dict[str, Any] | Noner  r  r  rl   r   r   )r;  r   rX  r   r=  r   rP  r   r?  r   rQ  r   rR  r  rS  r   r   r   )r  r   r   rn   )r  r   r   r   )rx   r   rv  r   rp  rn   r   r   )r   r   rp  rn   r   r   )r   r   rp  rn   r   r   )r   r   )r   r   r   r   r   r   )r   r   r   r   r   zOptional[float])r   r   r   r   rw   r   r   r   r   rn   r   r  )gQ?)r   r   r   r   r  rm   r   r   )r   r   r   r  )r   r   r  r   r   r   )
r   r   r   rn   r   rn   rp  rn   r   r  )r=   rm   r>   rm   r   r   )r   )rb   r   rK   r   r   r   )r   r   )r   r  r  zList[Dict[str, Any]]r   r   r  r   r  Optional[Dict[str, Any]]r  r  r   rl   )r  r   r   r   )r  r   rQ  r   r   r   rR  r  rS  r   r  r   r  rn   r   rn   r  r   r   r   )X__doc__
__future__r   r   loggingr   retypingr   r   r   r   	getLoggerr   r  r   	frozensetr^   r   r   r   r   r   r   compiler   r6  r+   r7  r8  r`   r9   MARKER_GEOMETRY_CONTRACT_VERSIONr   r/   rh   rp   r{   r   r   r   r   r   r   r  rN  rZ  r  r  r  r  r  r  r  r  r  r  ri  rk  rm   MARKER_ANNOTATE_CONTRACT_VERSIONMARKER_MAP_SIZEGEOMETRY_TEXT_VERSIONrx  r}  r  r  r  r   r  r  r  r   r  r  r  r  r  r  r  ru  r  r  r  r  r  r  r  r   r0   r   <module>r(     s   # 	   	 , ,			8	$	DE!Y],CDD 
  !T\\#& !T\\%(  &2::&9: 
# 
 
 

 
% , )  Sz C #&%  $%   &*&"
' X;@XX48XX9=X XvG
N #

#=> '''&/'>B'	'.@ =	=%.== 
=B /4hh'+hh9=h h 	hV=N ,0+/e
e e 	e
 e e e *e )e e eP
  	
      	B ) }	}
 	} 	} 	}< 	=}> 	?}J 	K}V 	W}f 	g}t 	
u}F 	
G}P 	
Q}^ 	
_}x 	
y}V 	
W}x 	
y} D 5 
 "  E  2 
 - 

$   4
E, $%  
 4   (- $148 */"&36* =B 
2 
	( 	L 	D   #$ M?24 4 4" **?	?+??8 !%J	J+J47JDGJJ JZ80x 7;'.3*Sl__14__D
L :?EFF26FF-1F FR/(8 H /3/3OO %O 	O
 O ,O -O 	Od%$,  "$ II I 	I
 I I I I I I 	IX
r0   