
    aji`                    :   d Z ddlmZ ddlZddlmZ ddlmZmZm	Z	m
Z
mZ  ej                  e      ZdZddd	Zd
ZdZdZdZdZefd*dZef	 	 	 d*dZefd+dZd,dZd-dZ ed      j9                  d      Zd.dZd/dZ	 	 d0	 	 	 	 	 d1dZ 	 	 	 	 	 	 d2dZ!dddd	 	 	 	 	 	 	 	 	 	 	 	 	 d3dZ"ef	 	 	 	 	 	 	 d4dZ#dZ$dZ%d-d Z&d5d!Z'ef	 	 	 d6d"Z(ef	 	 	 d7d#Z)ef	 	 	 d8d$Z*d,d%Z+ddd&	 	 	 	 	 	 	 	 	 d9d'Z,ef	 	 	 	 	 	 	 	 	 d:d(Z-dded	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d;d)Z.y)<u  confined(좁고 복잡한 조작 공간) 샷 — fp readback 부품 (2026-08-10).

설계 SOT: docs/superpowers/specs/2026-08-10-confined-interior-fp-readback-production-design.md
실험 SOT: backend/car_interior_ab_experiment.py + artifact/20260810_car_interior_ab/

자동차 전용이 아니다 — 전투로봇 콕핏·장갑차·버스·항공기·조타실 등
조작 장치가 많은 모든 밀폐 공간(사용자 확정 2026-08-10). 공간의 실체는
전부 브리프의 LOCATION/SHOT 텍스트가 정하고, 이 부품의 계약(팩)은
범용 어휘만 쓴다 — 특정 장치·차종·방향 하드코딩 금지.

흐름: ①정밀 fp 작화(gpt-image-2 — gemini 는 조작 장치 위치를 반복해서
틀린 실측, location_floor_plan 관례와 동일 부품) ②VLM readback(도면
읽기+브리프 대조 mismatches+카메라 시점 장면 설명 — 브리프 내부 모순
검출기 겸용) ③mismatch 시 gpt i2i 국소 수정 1회+재검증 ④생성에는
fp 도면+장면 설명을 함께 준다(잔류 금지 라벨).
    )annotationsN)Path)AnyDictListOptionalTupleconfined_fpz1.202608110030z2.202608110130)12r   confined_fp_v2_shared_baseuD  You decide whether a floor-plan layout aid would help generate this shot.

Answer `applies: true` ONLY if BOTH hold:
1. The shot takes place INSIDE an enclosed, control-heavy space — an
   operator's or crew's interior where WHO SITS WHERE relative to the
   controls matters (a vehicle cabin, cockpit, wheelhouse, armored crew
   bay, bus, aircraft, a machine's operator station) — OR the SHOT TEXT
   aims a directional object whose pointing must read correctly.
2. Getting the seating/station arrangement or that aim wrong would be
   an obvious, story-breaking mistake.

Answer `applies: false` when the space is a simple storage or open
interior whose object arrangement ordinary viewers cannot tell apart
(a straight photograph reads fine there), or when the shot is not
actually inside the space. Write `reason_ko` in Korean.	1536x1024u9   FLOOR PLAN — layout authority, a diagram, never sceneryc           	     n    	 t         |    S # t        $ r  t        d| dt        t                d      w xY w)Nz"unknown confined_fp pack selector z	 (known: ))CONFINED_FP_PACK_VERSION_MAPKeyError
ValueErrorsorted)selectors    V/Users/manta/Documents/Projects/TheRoad-I1/backend/app/modules/pipeline/confined_fp.pyresolve_confined_fp_packr   A   sM    
+H55 
0 =:;<A?
 	

s    )4c                :    ddl m}  |t        t        |             S )uG   팩 bytes 지문 (#77 관례 — 버전 문자열이 아니라 내용).r   )pack_dir_content_hash)app.modules.prompt_loaderr   _MODULEr   )r   r   s     r   confined_fp_pack_content_hashr   K   s     @ *B8*LMM    c                >    ddl m}  |t        | t        |            S )Nr   )load_prompt)version)r   r   r   r   )stemr   r   s      r   _loadr"   T   s!    57AC Cr   c            	     R    ddddddiddiddiddidg dddddidddid	g d	dS )
NobjectFtypestring)controlsmirrorscamera	occupantsr%   additionalProperties
propertiesrequiredarray)r%   items)reads
mismatchesscene_description_en r4   r   r   build_fp_readback_schemar5   [   sj    % !%!' 2 &1%x0"((!3	 K	 $+fh5GH%+X$6
 D! r   c                B   g }d}| j                  d      D ]w  }|j                         }|j                  d      rd}'|r>|j                  d      rd}=t        j	                  |      rd}|j                  |       fg|j                  |       y dj                  |      S )ur  브리프에서 CAMERA & FRAME 블록만 뺀 전문 — confined 생성 전용.

    실측(2026-08-10): 화면 배치 문자("운전대는 오른쪽")가 조작 장치 소속
    좌석의 결정론 없이 실리면 모든 경로가 반대 배치로 수렴한다. 구도
    권위는 fp+장면 설명이 대신한다. 브리프 저작 자체는 불변 — 제거는
    confined 생성 호출에서만(설계 결정 2).

    ★**끝나는 자리가 하나뿐이라 뒤가 통째로 사라졌다** (2026-08-27).
     종전에는 `Compose the frame` 으로 시작하는 줄에서만 멈췄다. 그런데
     그 문장은 팩 v6 에만 있고 **컴팩트 팩 v22 에는 없다** — grok 조립 +
     confined 경로에서 CAMERA 이후 프롬프트 **전부**가 잘린 채 유료 호출로
     갔다. 시네마틱 마감 절만이 아니라 그 뒤의 모든 절이.

     고치는 자리는 팩이 아니라 여기다. 컴팩트 스템에 종료 문구를 넣으면
     **나가는 v22 바이트가 바뀌어** 완주본이 재생성된다. 여기서 **다음
     top-level 헤딩**에서도 끝내면(그 헤딩은 **보존**) 나가는 문안은
     그대로고 잘리던 것만 안 잘린다.

     `Compose the frame` 갈래는 **그대로 둔다** — v6 로 조립한 브리프의
     바이트가 한 자도 안 바뀌어야 한다(그 줄은 종전처럼 버린다).
    F
CAMERA & FRAMETCompose the frame)splitstrip
startswith
_HEADER_REmatchappendjoin)promptoutin_camlnss        r   strip_camera_blockrF   p   s    , CFll4 HHJ<<()F||/0"

2

2! !" 99S>r   rez^[A-Z][A-Z &/()'-]{4,}c                   dddddddg }d}dg dfd}| j                  d      D ]  }|j                         }rt        j                  |      r |        |j	                  d      r|d<   I|j	                  d      r|d	<   `|j	                  d
      r |        d|gc|j	                  d      r |        d|gc|j	                  d      rd}|j                  |       |j	                  d      r|d<   d}|r(|j                  |       |j	                  d      sd}s
j                  |         |        dj                  |      d<   S )u  브리프 고정 헤더 절 발췌(팩 헤더 문구의 형식 소비) — fp 입력용.

    없는 절은 빈 문자열(브리프 구성은 샷마다 다르다 — 발췌는 입력을
    좁히는 용도이고 생성은 전문을 따로 받는다).

    carried/people (v2): 인물이 어느 자리에 있는지의 원천 절 — fp 샷
    마커·readback 입력에 없으면 뒷좌석 인물이 도면에서 빠진다(실측).
    두 절은 헤더 줄부터 다음 대문자 헤더 전까지의 블록.
     )	shot_textlocationr)   lightingcarriedpeopleFc                 X    r"dj                         j                         <   dg c y )Nr7   rI   )r@   r;   )block	block_keyrB   s   r   flushz%extract_brief_sections.<locals>.flush   s-    !YYu-335C	Nr	5r   r7   z	SHOT TEXTrJ   LOCATIONrK   zCARRIED STATErM   zPEOPLE:rN   r8   Tz- LIGHTING & MOODrL   r9   r)   )returnNone)r:   r;   r=   r>   r<   r?   r@   )	rA   camrC   rR   rD   rE   rP   rQ   rB   s	         @@@r   extract_brief_sectionsrW      s^    bbB(CCFIE" ll4 HHJ))!,G<<$ C\\*%C
O\\/*G(1#Iu\\)$G'!Iu\\*+FJJqM\\-.C
OFJJqM||/0LL9 !: 
GIIcNCMJr   c                    dj                  d | j                  d      | j                  d      | j                  d      | j                  d      | j                  d      | j                  d      fD              S )	N

c              3  $   K   | ]  }|r| 
 y wNr4   .0xs     r   	<genexpr>zfp_context.<locals>.<genexpr>   s"      @ #:Q >?  #:   rJ   rM   rN   rK   r)   rL   )r@   get)sectionss    r   
fp_contextrc      sm     ;; @[!8<<	#:XZ 8XZ 8#: @ @ @r   c           
     F   ddl m} ddlm} ddlm} ddlm}  |t        |j                              } |d        |||rd	nd
| |r|gnd|j                  t        dd|      }	|	st        d      |j                  j                  dd       |j                  |	       y)u=   fp 작화·수정 — gpt-image (기록은 부품에 내장).r   settings)openai_client)call_gpt_image_bytes)reserve_current_call)timeoutzconfined_fp.draw)sourceeditgenerateNhigh)modelsizequality)moderA   	ref_pathscall_kwargscapture_roleu%   confined_fp: gpt fp 생성 빈 응답T)parentsexist_ok)app.core.configrf   app.core.openai_keysrg   #app.modules.llm.gpt_image_primitiverh   app.core.image_call_budgetri   floatllm_timeout_image_genopenai_image_modelFP_SIZERuntimeErrorparentmkdirwrite_bytes)
rA   out_pathrefru   rf   rg   rh   ri   clientpngs
             r   _gpt_fp_imager      s     )2H?5)G)G#HIF 23
V
3%D&99$9!#C BCCOO$6r   c           	     j   ddl m}m ddlm}m} t        d      dd|z   dddd ||      gt               dfd	}	  || d
      S # |$ rX}	t        j                  d| ||	       i }
 ||  d|d|
      }i |dd
t        |	      ||
j                  d      dicY d }	~	S d }	~	ww xY w)Nr   )EmptyLLMResponsecall_structured)SELECT_JUDGE_MODEL_2png_partfp_readback_systextzLOCATION / CAMERA / SHOT TEXT:
)r%   r   zTHE FLOOR-PLAN DIAGRAM:c                >     | fi xs i | d|iiddd|S )Nro   confined_fp_readbackFproject_configschema_nameopik_metadataenable_fallbackr4   )	tagro   sealr   r   partsr   schemasys_ps	      r   _askz_readback.<locals>._ask  sJ    v+L~3LcGU;KL.'!+
 &*+ 	+r   
gemini-prou[   confined_fp readback[%s]: 첫 관찰 빈 응답 — 둘째 관찰자(%s)로 한 번 더: %s
_observe_2)num_retries
usage_sinkreadback_fallbackphysical_model)first_modelfirst_errorro   r   )r   strro   r   r   r   rT   Dict[str, Any])app.modules.llm.llm_clientr   r   %app.modules.pipeline.multiroll_geminir   r   r"   r5   loggerwarningr   ra   )step_tagfp_pathctxr   r   r   r   r   r   first_sinkrbr   r   r   r   s      ``       @@@@r   	_readbackr      s     M
 #$E83>@&?@g E &'F+ +Hl++  	')=u	F !#XJj)+?E3" )'u:)#ii(89	,
  	!s   A B2AB-'B2-B2r   )r   r   pack_versionc                   t        |      }t        |      }|j                         st        d|  d      t	        d|      }||  dz  }	||  dz  }
t        |dz   |z   |	       t        d|  |	|||      }d}|j                  d	      rot        j                  d
| t        |d	                t        t	        d|      dz   dj                  |d	         z   dz   |z   |
|	       t        d|  d|
|||      }d}nddl}|j                  |	|
       t        |j                  d      xs d      j                         }|st        d|  d      |
i |d|ifS )u>  [v1 흐름 — 팩 v1 전용] 샷 단독 fp 산출 — (fp_path, readback dict).

    v2(base 공유+샷 마커: `produce_base_fp`+`produce_shot_fp`)가 현행이다.
    이 함수는 v1 팩 스템(fp_head) 전제라 selector "1" 고정 — 기록·재현용
    으로 남긴다(기존 모듈 삭제 금지 관례).

    readback dict = {reads, mismatches, scene_description_en, fixed}.
    mismatches 는 durable record 로 남길 것(브리프 내부 모순 보고 포함 —
    실측: LOCATION 문자 vs 미러 반사 요구의 모순을 정확히 보고했다).
    confined_fp[uP   ]: 브리프에서 발췌 절 0 — fp 를 그릴 입력이 없다 (fail-closed)fp_headz_fp_draft.pngz_fp.pngrY   confined_fp_readback_Fr2   u9   confined_fp[%s]: fp 어긋남 %d건 — i2i 국소 수정fp_fix_head
- )r   _fixTr   Nr3   rI   [   ]: 장면 설명이 비었다 — 배치 권위 없이 생성하지 않는다 (fail-closed)fixed)rW   rc   r;   r   r"   r   r   ra   r   infolenr@   shutilcopyr   )r   briefout_dirr   r   r   rb   r   headbase_fpr   r   r   r   descs                  r   produce_confined_fpr   -  s   & &e,H
X
C99;3%  # #$ 	$ L)D3%}--G3%w'G$-#%w/	*3%0'3!=
2BE	vvlGR%&	( 	-.7kk"\*+,.457:;	" .se48'3%}6GW%rvv,-34::<D3%  3 34 	4 *r*7E***r   c                |    t        d|      j                         dz   | j                         z   dz   t        |      z   S )u  confined 생성 프롬프트 — gen_head + 장면 설명 + 정리된 브리프.

    브리프에서 CAMERA 절과 prev 참조 전제 절을 뺀다(strip_confined_brief)
    — 실제 첨부([fp, 엔티티])와 지시가 일치해야 한다 (Codex BLOCK-4).
    gen_headzG

SCENE LAYOUT (what a careful reader saw in the attached floor plan):
rY   )r"   r;   strip_confined_brief)r3   r   r   s      r   build_confined_gen_promptr   d  sP     	j,'--/	/557	8 	 (.	/r   )z%THIS SHOT CONTINUES THE PREVIOUS SHOTzPREVIOUS STILL USAGEzSTRUCTURE LOOK AUTHORITYu    The shot takes place here — c                   g }d}t        |       j                  d      D ]  }|j                         |rt        j	                        rd}t        fdt        D              rd}G|rJj                  d      r$t        |v r|j                  t        d      d   dz   }|j                  |        dj                  |      S )	u  confined 생성용 브리프 정리 — 첨부 전제 절·꼬리 제거.

    제거: ①CAMERA 절 ②prev 참조 전제 절 2종 ③STRUCTURE LOOK 권위 절
    ④LOCATION 줄의 첨부 사진 권위 꼬리(장소 텍스트는 보존). 실제 첨부와
    지시가 일치해야 한다 — 세 실조립 모드(default/structure_seed/seed_bg)
    회귀로 잔존 0 을 잠근다.
    Fr7   c              3  @   K   | ]  }j                  |        y wr[   )r<   )r]   hrE   s     r   r_   z'strip_confined_brief.<locals>.<genexpr>  s     F(E1q||A(Es   TrS      r   z The shot takes place here.)rF   r:   r;   r=   r>   any_NON_ATTACHED_AUTHORITY_HEADSr<   _LOCATION_TAIL_MARKERr?   r@   )rA   rB   in_droprD   rE   s       @r   r   r     s     CG (..t4HHJz''*GF(EFFG<<
#(=(C((0!4Q712B

2 5 99S>r   c                     dd l }|j                         }| D ]<  }|j                  t        |      j	                  d             |j                  d       > |j                         d d S )Nr   zutf-8       )hashlibsha256updater   encode	hexdigest)r   r   r   ps       r   _sha16r     sU    A	Qw'(	  ;;="r   c                    ddl }ddlm} t        | t        |j                  t               d      |j                  |      S )u  적용 판별 캐시의 입력 지문 — 계약·스키마·물리 모델·브리프 전문.

    존재 캐시는 새 설정을 옛 판별로 봉인한다(Codex BLOCK-2 — #77 과
    같은 부류). 재료가 하나라도 바뀌면 재판별한다.
    r   Nre   T)	sort_keys)jsonrx   rf   r   APPLICABILITY_SYSdumpsbuild_applicability_schemagemini_text_model)r   r   _jsonrf   s       r   apt_fingerprintr     s;     (%*++8:d+K,,l< <r   c                Z    ddl m} t        | t        d|      |j                  t
              S )uZ   base 도면 캐시의 입력 지문 — 장소 절+작화 스템 bytes+gpt 모델·크기.r   re   base_fp_head)rx   rf   r   r"   r~   r   )
place_textr   rf   s      r   base_fingerprintr     s*     )*eNLA--w8 8r   c           
     4   ddl }ddl}ddlm} |j	                  t        |       j                               j                         }t        ||j                  |dd      t        d|      t        d|      t        d	|      |j                  |j                        S )
uX   샷 fp 캐시의 입력 지문 — base bytes+절 전문+스템 3종+물리 모델 2종.r   Nre   TF)r   ensure_asciishot_fp_mark_headr   r   )r   r   rx   rf   r   r   
read_bytesr   r   r   r"   r~   r   )	base_pathrb   r   r   r   rf   
base_bytess          r   shot_fingerprintr     s     (Y : : <=GGIJ*++h$U+K+\:)<8|4--x/I/IK Kr   c                 "    ddddiddidddgd	S )
Nr$   Fr%   booleanr&   )applies	reason_kor   r   r+   r4   r4   r   r   r   r     s0    %	* (+
 , r   )r   r   c          
     n    ddl m} d|  } ||t        d|z   t               i |xs i |ddiid|d	      S )
u  fp 적용 판별 — confined 샷 중 fp 가 돕는 샷만 (LLM 구조화 판정).

    창고류 단순 공간·공간 외부 샷은 false → 기존 경로 그대로. 호출측은
    결과를 record 에 캐시해 재개 시 재판별 지출을 막을 것.
    r   )r   confined_fp_apt_zSHOT BRIEF (full):
ro   r   confined_fp_aptFr   )r   r   r   r   )r   r   r   r   r   r   s         r   judge_fp_applicabilityr     s]     ;!#'H#&"$;>/R ; 7L"9;%# r   c                    |xs dj                         st        d|  d      t        t        d|      dz   |z   |d       |S )u  공간 단위 base 도면 — 좌석 배열+조작 장치(+조건부 미러)만.

    같은 공간의 샷들이 이 한 장을 공유한다(샷마다 다른 공간을 그리던
    결함 제거). 캐시 키·재사용은 호출측 몫(장소 단위).
    rI   r   uH   ]: LOCATION 절이 비어 base 도면을 그릴 수 없다 (fail-closed)r   rY   confined_fp_base)ru   )r;   r   r   r"   )	space_taglocation_textr   r   s       r   produce_base_fpr     s^     R&&(9+ &. ./ 	/ nl+f4}D13 Or   c          	     0   dj                  d |j                  d      |j                  d      |j                  d      |j                  d      fD              }|j                         st        d|  d      t	        |      }t        t        d	|      dz   |z   ||d
       t        d|  ||||      }	d}
|	j                  d      rt        j                  d| t        |	d                t        t        d|      dz   dj                  |	d         z   dz   |z   ||d       |	j                  d      }t        d|  d||||      }	|ri |	d|i}	d}
t        |	j                  d      xs d      j                         }|st        d|  d      |i |	d|
ifS )u  샷 마커 도면 — base 에 카메라·인물·방향성 소품 마커만 i2i 로.

    마커 입력에 인물 배치 절(CARRIED STATE·PEOPLE)을 반드시 동반한다
    (v2 실측: 없으면 뒷좌석 인물이 도면에서 빠진다). readback 검증 후
    mismatch 가 있으면 지적을 지시로 i2i 국소 수정 1회+재검증.
    반환 = (fp_path, readback dict{reads,mismatches,scene_description_en,
    fixed}) — mismatch 는 record 로 남길 것(브리프 모순 보고 포함).
    rY   c              3  $   K   | ]  }|r| 
 y wr[   r4   r\   s     r   r_   z"produce_shot_fp.<locals>.<genexpr>!  s      > '8;<  '8r`   rJ   rM   rN   r)   r   u/   ]: 마커 입력 절이 비었다 (fail-closed)r   confined_fp_mark)r   ru   r   Fr2   uA   confined_fp[%s]: 샷 도면 어긋남 %d건 — i2i 국소 수정r   r   confined_fp_fixr   r   readback_fallback_initialTr3   rI   r   r   )r@   ra   r;   r   rc   r   r"   r   r   r   r   r   )r   r   rb   r   r   r   r   mark_ctxr   r   r   _initial_fbr   s                r   produce_shot_fpr    s   $ {{ >[!8<<	#:XX 6'8 > >H >>3%NOQ 	Q
X
C!<069HDg,>@ 
*3%0(C!=
2BE	vvlOR%&	( 	-.7kk"\*+,.457:;(1B	D ff01.se48(C%}6ABA3[ABrvv,-34::<D3%  3 34 	4 ++GU+++r   )r   r   rT   r   )r!   r   r   r   rT   r   )rT   r   )rA   r   rT   r   )rA   r   rT   Dict[str, str])rb   r  rT   r   )Nr
   )
rA   r   r   r   r   zOptional[Path]ru   r   rT   rU   )r   r   r   r   r   r   r   Optional[Dict[str, Any]]r   r  rT   r   )r   r   r   r   r   r   r   r  r   r  r   r   rT   Tuple[Path, Dict[str, Any]])r3   r   r   r   r   r   rT   r   )r   r   rT   r   )r   r   r   r   rT   r   )r   r   r   r   rT   r   )r   r   rb   r  r   r   rT   r   )
r   r   r   r   r   r  r   r  rT   r   )
r   r   r   r   r   r   r   r   rT   r   )r   r   r   r   rb   r  r   r   r   r  r   r  r   r   rT   r  )/__doc__
__future__r   loggingpathlibr   typingr   r   r   r   r	   	getLogger__name__r   r   r   CONFINED_FP_PACK_VERSIONCONFINED_FP_POLICY_VERSIONr   r   FP_REF_LABELr   r   r"   r5   rF   
__import__compiler=   rW   rc   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r4   r   r   <module>r     s    #   3 3			8	$
 

 
     : ;   K .F 
 -NNN &> C*)X %%&?@
6r@ )-&3% #8<4/6/5/:H/n 04.24+	4+ 4+ 	4+
 -4+ ,4+ 4+ !4+t 1  		,!  : 4 )A<"%<EH<" *B8#&8FI8 *BK#&KFIK& 04.2	 -	
 , < 1	  	
 
6 04.205,	5,5, 5, 	5, -5, ,5, 5, !5,r   