
    \jKk              
      t   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	m
Z
  ej                  e      ZdZded<   dZded	<   d
Zded<   dZddg dddddddddg dddg dddZddeddddidddg ddd dd!dd"g d"ddZd#ed$<   dd%d&ddd'ddd(ddd)ddd*ddedd+g d+ddZd#ed,<   d-Zd.Zddd/ddeddd0ddd1idd2dd3d4d5gddd6d7g d7ddZd#ed8<   d9Z	 dM	 	 	 	 	 	 	 	 	 dNd:Z	 	 	 dO	 	 	 	 	 	 	 	 	 	 	 	 	 dPd;Zdd<dddd1iddid=d>d?gdddid<gddZd#ed@<   dAZdBZ	 	 	 	 	 	 dQdCZ	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dRdDZ 	 	 dSdEdF	 	 	 	 	 	 	 	 	 	 	 dTdGZ!	 	 dSdEdF	 	 	 	 	 	 	 	 	 	 	 dTdHZ"	 	 	 	 	 	 	 	 dUdIZ#	 	 	 	 	 	 	 	 	 	 dVdJZ$	 	 dSdEdF	 	 	 	 	 	 	 	 	 	 	 	 	 dWdKZ%	 	 dS	 	 	 	 	 	 	 	 	 	 	 	 	 dXdLZ&y)YuJ  W21B-W7 (2026-06-12) — visual_continuity_anchor LLM IO boundary.

스파이크 2개(VISUAL_ACCEPTANCE_PASS)의 anchor 추출 프롬프트 production 포팅:

  - ``extract_zoom_anchor`` — 스파이크1 (S12 zoom) ANCHOR_SYS. 같은 순간의
    wide/zoom continuity group 에서 양쪽에 동일해야 하는 시각 요소를
    evidence quote 와 함께 추출한다.
  - ``extract_prop_anchor`` — 스파이크2 (S25/S29 prop) ANCHOR_SYS 의 generic 화.
    printed/displayed content 를 가진 물리 prop 의 단일 결정판 identity 를
    원문 전체에서 통합 추출한다. "photograph" 고정 표현 제거 — prop 종류는
    입력 데이터가 결정한다. 인쇄 문구의 자구(텍스트 의미)는 고정하지 않고
    printed content 의 구조(장소+인물 구성)만 고정한다 (Codex 가이드 7).

규칙 (CLAUDE.md / 절대 규칙):
  - 입력 시나리오/씬 텍스트는 절대 자르지 않는다 — 원문 전체 전달.
  - 프롬프트에 작품 고유명사/특정 시나리오 토큰 0 — 모든 내용은 caller 가
    조립한 데이터로만 들어온다.
  - 출력 품질은 deterministic 테스트 비대상 — canary + 육안 gate.

모델 라우팅은 ``call_structured(step="visual_continuity_anchor")`` —
manifest default_model(gpt=gpt-5.5) + project_config override 를 따른다.
    )annotationsN)AnyDictListOptionalTuplevisual_continuity_anchor_v1strPROVIDER_VERSIONz4.202606201500PROMPT_VERSIONi  intANCHOR_MAX_TOKENSvisual_continuity_anchorobjectstring)subject_pose	held_prop
prop_statesurface_state)typeenumz]one English sentence stating the locked visual fact (position, orientation, which hand, etc.))r   descriptionz>verbatim quote from the scene text or staging that supports it)highmediumlow)kindr   evidence_quote
confidenceF)r   
propertiesrequiredadditionalPropertiesarray)r   itemsr   zlEnglish sentences: what the WIDE shot must visibly include so that the zoom shot is a true enlargement of it)r   r#   r   )same_axis_punch_indifferent_camera_same_moment	uncertainzwhether the zoom frame is a sub-region of the wide frame as staged (same camera position/axis, only tighter) or staged from a different camera position/angle/height)r   r   r   zaverbatim quotes from the two shots' staging/descriptions that support the camera_relation verdict)locked_elementswide_shot_contractcamera_relationcamera_relation_evidenceDict[str, Any]ZOOM_ANCHOR_SCHEMAbooleana  true ONLY if THIS prop is itself an object that bears its own printed or displayed content whose composition must stay consistent across shots (e.g. a photograph, map, document, screen, printed sheet/sign). false for a painted/drawn mark or symbol, an emblem, a tool, fabric, a plant, a vessel, or any object that has no printed/displayed matter of its own. Default to false when unsure.zone English sentence (with a verbatim scene-text quote when carries_printed_content is true) justifying the verdict; for false, state briefly why the prop bears no printed contentu   when carries_printed_content is true: one or two English sentences fully describing what is printed/displayed on THIS prop — the place AND every person visible in it, integrated into one coherent image. When false: empty string.zQEnglish: object type, real-world physical size class, material/wear state, stainsz|one English sentence usable in any shot prompt stating the prop must keep its real physical size relative to the environment)carries_printed_contentapplicability_evidenceprinted_contentphysical_formscale_contractr'   PROP_ANCHOR_SCHEMAu  You are a film continuity supervisor. Two shots form a CONTINUITY GROUP: they depict the SAME MOMENT in the same space — one wide framing, one zoomed-in detail of that frame.

From the scene text (source of truth) and the two shot descriptions/staging, extract the visual elements that MUST be identical across both shots.
Rules:
- Only elements grounded in the provided text (no invention). Every locked element carries a verbatim evidence quote.
- The zoom shot's focal subject MUST exist in the wide shot's contract.
- Keep it minimal — only elements whose mismatch would break continuity.

Also judge the CAMERA RELATION between the two shots from their staging and descriptions. The question is practical: could a tighter CROP of the wide frame serve as this zoom shot without violating the zoom's ESSENTIAL staging? Essential staging means which side of the subject is seen (frontal vs profile vs back), above vs below relation, and what occupies the frame — NOT exact camera position; minor differences in distance, height or lateral position do not matter.
- 'same_axis_punch_in': a crop of the wide frame can serve the zoom — the zoom's focal region is visible inside the wide frame from a compatible side/angle.
- 'different_camera_same_moment': the zoom's essential staging cannot come from the wide camera's viewpoint (e.g., the zoom needs a frontal face but the wide sees a profile, or the opposite side of the subject, or looking up where the wide looks down) — the same moment seen by a genuinely different camera.
- 'uncertain': the staging does not give enough to decide.
Quote the staging passages that support your verdict in camera_relation_evidence.u.  You are a film props master. One physical prop (THIS prop, given by its canon) recurs across multiple shots. First CLASSIFY it, then — only if it qualifies — lock its displayed-content identity.

STEP 1 — carries_printed_content (default false):
Set true ONLY if THIS prop is itself an object that bears its own printed or displayed content whose composition must stay consistent (a photograph, map, document, screen, printed sheet or printed sign). Set false for a painted/drawn mark or symbol, an emblem, a tattoo, a tool, fabric, a plant, a vessel, or any object that has no printed/displayed matter of its own. When unsure, choose false. Justify in applicability_evidence.

STEP 2 — if false: set printed_content, physical_form and scale_contract to empty strings and locked_elements to []. Invent nothing.

STEP 2 — if true: produce the SINGLE definitive identity of THIS prop, grounded ONLY in THIS prop's own canon and its own appearances in the scene texts.
Rules:
- Describe ONLY this prop. NEVER borrow, merge, or describe the content of a DIFFERENT co-occurring prop, even if a similar shape, colour or word appears on another object. If the texts only describe a similar feature on a different object, this prop is NOT that object — prefer false.
- printed_content merges ALL grounded facts about what THIS prop shows (place + people), integrated into one coherent image. No invention beyond the texts.
- If a person shown on the prop matches one of the provided character canons, describe them consistently with that canon.
- Do NOT fix the literal wording of any text visible on the prop — fix only the STRUCTURE of the printed content (the place and the people composition). Written characters are allowed to vary between renders.
- Every locked element carries a verbatim evidence quote from the scene texts.u  one or two English sentences stating the ONE physical configuration of the immobilized subject that every shot must keep consistent: body posture/orientation, where the body lies/sits, which way the head/limbs point, and the state of any held or directly-adjacent object. Refer to the subject only structurally (e.g. 'the immobilized subject', 'the body') and by screen position — NEVER by a proper name or entity ID.u   for EACH member shot, what part of the immobilized subject (and its held/adjacent props) is actually inside that shot's frame — so a tight insert is not forced to show the whole body. Whatever is off-frame still obeys shared_state_contract.integerzMEnglish: which portion of the subject/props is visible in this shot's framing)
shot_indexvisible_focusr5   r6   )r   r   r#   )shared_state_contractr'   per_shot_visible_focusIMMOBILIZED_SUBJECT_SCHEMAu  You are a film continuity supervisor. One character is in an IMMOBILIZED state (dead, unconscious, or severely injured — they cannot move on their own) and appears across several shots of the SAME scene. Because the subject cannot move, its body posture, position, orientation and the state of anything it holds or that lies against it MUST stay identical from shot to shot — only the camera framing changes (a later shot may crop in to a hand or a detail).

From the scene text (source of truth) and the shot descriptions/staging (each shot carries its own independently-written body_pose, which is exactly what drifts), produce the SINGLE shared physical configuration of this subject.
Rules:
- shared_state_contract: the one configuration all shots share. Ground it ONLY in the provided text — no invention.
- Refer to the subject structurally ('the immobilized subject', 'the body', 'the left hand') and by screen position. NEVER use a proper name or an entity ID token (like C01) in any output field — the renderer identifies the subject from its reference image, not from a name.
- locked_elements: the minimal set of visual facts whose mismatch would break continuity (posture, which hand, held/adjacent prop state, surface). Every locked element carries a verbatim evidence quote from the scene text or staging.
- Do NOT lock camera, framing, lens or composition — those are free per shot. Lock only the subject's physical state.
- per_shot_visible_focus: for each member shot, state which part of the subject is actually in frame for that shot's framing, so a tight insert is not forced to render the whole body.
- If prior continuity evidence from a related zoom group is given, treat it as a hint only — the scene text remains the source of truth.c                   d| z   g}|j                  dt        j                  dD ci c]  }||j                  |       c}d      z          |j                  d       |D ]4  \  }}}}	}
|j                  d| d| d	|
xs d
 d|xs d d|	xs d
 
       6 |r)|j                  dt        j                  |d      z          dj	                  |      S c c}w )u,  scene_text: 원문 전체(자르지 않음). subject_canon: {short_id, name} (식별용 —
    출력엔 이름 금지). member_shots: [(scene_index, shot_index, description,
    body_pose, framing_scale)]. prior_zoom_locked: 같은 순간 zoom 그룹 locked_elements
    (참고자료, SOT 아님).SCENE TEXT (full):
uZ   
IMMOBILIZED SUBJECT (identity for your understanding only — do NOT name it in output):
)short_idnameFensure_asciiz)
SHOTS WHERE THIS SUBJECT IS IMMOBILIZED:- scene  shot z
 [framing=zn/az]:
    description:  uP   
    this shot's written body_pose (may drift — reconcile into ONE contract): zU
PRIOR CONTINUITY EVIDENCE from a related zoom group (HINT only, not authoritative):

appendjsondumpsgetjoin)
scene_textsubject_canonmember_shotsprior_zoom_lockedpartsksishidesc	body_poseframings              l/Users/manta/Documents/Projects/TheRoad-I1/backend/app/modules/pipeline/visual_continuity_anchor_provider.py%build_immobilized_subject_user_promptrV     s
    $j01E	LL	
**.BC.BQ!!!$$.BC
	
 
LL=>-9)Cy'rd&Z0@5/A B  $
| ,^!E"$	
 .:  "&**->U"ST	
 99U# Ds   Cc           
     f    ddl m}  |t        t        t	        | |||      t
        |d|t              S )Nr   call_structuredimmobilized_subject_anchorstepsystem_promptuser_promptresponse_schemaproject_configschema_nameopik_metadata
max_tokens)app.modules.llm.llm_clientrY   _STEPIMMOBILIZED_SUBJECT_SYSTEMrV   r9   r   )rJ   rK   rL   rM   r`   rb   rY   s          rU   "extract_immobilized_subject_anchorrg   :  s=     ;09|5F
 3%0#$     revised_prompts)variation_indexrevised_promptrj   rk   REVISED_WIDE_SCHEMAu  You revise T2I prompts for the WIDE shot of a zoom-continuity group. Keep each original prompt's camera position, framing, mood and environment description AS-IS. Only weave in the wide_shot_contract elements and locked_elements so every locked visual fact is explicitly visible in the wide frame. One moment, one space.
Hard rules:
- Do NOT add new characters, events or locations.
- Do NOT add any person who is not already present in the original prompt — not by entity ID, not by name, not by plain-text description. A person added without their reference image gets an invented face. If a locked element or contract item could only be satisfied by adding such a person, leave that element out and keep the original framing instead.
- Do NOT introduce any entity ID tokens (like C01, P02, L03) that are not already present in the original prompt, and do NOT add new requirements to use reference images — express the locked facts in plain text only.
- Revise EVERY prompt given, keeping its variation_index.u!  You revise T2I prompts for the ZOOM shot of a zoom-continuity group. This shot is staged from its own camera position/angle (NOT a pixel crop of the wide shot), so keep each original prompt's camera position, angle, height, framing, composition, lighting and mood AS-IS — they are this shot's camera plan. Only weave in the locked_elements so every locked visual fact (poses, held props, prop/surface states, costumes) matches the wide shot of the same moment. One moment, one space.
Hard rules:
- Do NOT add new characters, events or locations.
- Do NOT add any person who is not already present in the original prompt — not by entity ID, not by name, not by plain-text description. If a locked element could only be satisfied by adding such a person, leave that element out.
- Do NOT introduce any entity ID tokens (like C01, P02, L03) that are not already present in the original prompt, and do NOT add new requirements to use reference images — express the locked facts in plain text only.
- Revise EVERY prompt given, keeping its variation_index.c                    dg}| D ]  \  }}|j                  d| d|         |j                  dt        j                  |d      z          dj                  |      S )NzORIGINAL WIDE T2I PROMPTS:z[variation_index z]
z	
ANCHOR:
Fr>   rC   )rE   rF   rG   rI   )
variationsanchorrN   idxprompts        rU   build_rewrite_wide_user_promptrr     s_     **E!V(S9: "	LLF!GGH99Urh   c           
        ddl m}  ||| t        ||      t        |||t              }|j                  dg       D 	ci c]C  }	t        |	t              r1t        |	j                  d      t              rt        |	d         |	d   E c}	S c c}	w )Nr   rX   r[   ri   rk   rj   )
rd   rY   rr   rl   r   rH   
isinstancedictr
   r   )
r]   ra   rn   ro   r`   rb   r\   rY   resultrs
             rU   _rewrite_promptsrx     s     ;#2:vF+%#$	F -r22Aa:aee4D.Es#K 	A !1%5#662  s   ABzoom_continuity_anchor)r\   c          	     ,    t        t        d| ||||      S )uN   wide 멤버의 t2i variations 일괄 재작문 — {variation_index: revised}.revised_wide_prompts)rx   REWRITE_WIDE_SYSTEMrn   ro   r`   rb   r\   s        rU   rewrite_wide_promptsr~     s"     3FNM4 rh   c          	     ,    t        t        d| ||||      S )u   different_camera zoom 멤버의 t2i variations 재작문 — camera plan 보존 +
    locked facts weave (Cinematography 축). {variation_index: revised}.revised_zoom_prompts)rx   REWRITE_ZOOM_SYSTEMr}   s        rU   rewrite_zoom_promptsr     s"     3FNM4 rh   c                ~   d| z   g}d|fd|ffD ]  \  }}|j                  d| d|j                  d      xs dz          |j                  d      /|j                  | d	t        j                  |d   d
      z          |j                  d      s|j                  | d|d   z           dj	                  |      S )uX   wide/zoom: {description, staging, t2i_prompt(optional)} — 원문/입력 전체 전달.r;   WIDEZOOMrC   z SHOT description: r   rB   stagingz SHOT staging: Fr>   
t2i_promptz SHOT current t2i prompt:
)rE   rH   rF   rG   rI   )rJ   widezoomrN   labelshots         rU   build_zoom_anchor_user_promptr     s     $j01E7tr% 348O8USUVW88I*LLE7/2::d9oEJK L88L!LLE7"=>lASST 8 99Urh   c                <   g }| D ]  \  }}|j                  d| d| d        |j                  dt        j                  dD ci c]  }||j                  |       c}d      z          |rW|j                  dt        j                  |D cg c]"  }dD ci c]  }||j                  |       c}$ c}}d      z          |r6|j                  d	       |D ]   \  }}	}
|j                  d
| d|	 d|
        " dj	                  |      S c c}w c c}w c c}}w )uT   scene_texts: [(scene_index, 원문 전체)]. canon 은 {short_id, name, t2i_prompt}.zSCENE z TEXT (full):
rC   z(CURRENT PROP CANON (may be incomplete):
)r<   r=   r   Fr>   zQ
CHARACTER CANONS (use for consistent identity of any person shown on the prop):
z
SHOTS WHERE THE PROP APPEARS:r@   rA   z: rD   )scene_texts
prop_canoncharacter_canonsmember_shot_descriptionsrN   rP   textrO   crQ   rR   s              rU   build_prop_anchor_user_promptr     s=    EDvbTb9:  	LL3
**+MN+MaQ
q!!+MN
	
 ajj .- +MM*LQQa[*LM- #		
  675MBTLL8B4vcU"TF;< 699U' O Ns   D	DD0DDc          
     \    ddl m}  ||t        t        | ||      t        |d|t
              S )Nr   rX   zoom_anchorr[   )rd   rY   ZOOM_ANCHOR_SYSTEMr   r,   r   )rJ   r   r   r`   rb   r\   rY   s          rU   extract_zoom_anchorr     s5     ;(1*dDI*%!#$	 	rh   c           
     f    ddl m}  |t        t        t	        | |||      t
        |d|t              S )Nr   rX   prop_anchorr[   )rd   rY   re   PROP_ANCHOR_SYSTEMr   r3   r   )r   r   r   r   r`   rb   rY   s          rU   extract_prop_anchorr   )  s>     ;(1%57O
 +%!#$ rh   )N)
rJ   r
   rK   r+   rL   $List[Tuple[int, int, str, str, str]]rM   Optional[List[Dict[str, Any]]]returnr
   )NNN)rJ   r
   rK   r+   rL   r   rM   r   r`   Optional[Dict]rb   r   r   r+   )rn   List[Tuple[int, str]]ro   r+   r   r
   )r]   r
   ra   r
   rn   r   ro   r+   r`   r   rb   r   r\   r
   r   Dict[int, str])NN)rn   r   ro   r+   r`   r   rb   r   r\   r
   r   r   )rJ   r
   r   r+   r   r+   r   r
   )
r   r   r   r+   r   List[Dict[str, Any]]r   List[Tuple[int, int, str]]r   r
   )rJ   r
   r   r+   r   r+   r`   r   rb   r   r\   r
   r   r+   )r   r   r   r+   r   r   r   r   r`   r   rb   r   r   r+   )'__doc__
__future__r   rF   loggingtypingr   r   r   r   r   	getLogger__name__loggerr   __annotations__r   r   re   _LOCKED_ELEMENT_SCHEMAr,   r3   r   r   r9   rf   rV   rg   rl   r|   r   rr   rx   r~   r   r   r   r   r    rh   rU   <module>r      s;  , #   3 3			8	$5 # 5 ' & 3 "
  P

 G
 [
  (1JK  H!' . $+6LMh'W
 
3
 S%
/: "G$& N $N  G	$
 X#
 P
 9
 -
 %,6LMG$JZ!S*& N *^X 6U B  +
"
 %,6LM\
 !#)9"5 ((H& *?;(-#
!D W!K&. N &R@ D 9=	  !  7  6	 
 	 N 9=%)$(! 7 6	
 # " 2  (.	':'-x&8 /0@A(-
 ##!#' ^ 4@ *@ &% 	 & 	
 # "  > &*$(	 )% # "	  $ &*$(	 )% # "	  &
  		" &   +  9	 
 	 T &*$( )
  #	
 "  8 &*$(& + 9	
 # " rh   