
    ֓j6                        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ZdZdZd	d
dZdZddZddZddZddZddZ	 	 	 	 	 	 	 	 ddZdddddd	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddZy)u  background_share_plan — 에피소드 전체 배경 공유·참조 계획 (2026-07-19).

재설계 B-2 (사용자 확정): '비슷한 배경이 있는 곳은 배경 하나(seed 기반)를
만들고, 그 뒤 샷들은 전부 이전 샷 참조' — 샷별 개별 판정이 아니라
에피소드 전체를 조망하는 LLM 계획으로 '배경 참조 vs 앞쪽 샷 참조'를
구분 지휘한다. 씬 경계 무관 재방문 포함, 장소 동일성 판단=LLM(인용
근거 의무). 스틸 소비 시 이 계획이 shot_ref_classify prev 판정의 상위
권위(계획 부재=기존 판정 fail-safe).

결정론 검증(fail-closed): 전 선택 샷 커버·중복 0, 그룹 첫 샷=background,
prev 앵커=같은 그룹의 스토리상 앞 샷, 인용 실재(whitespace 정규화).
    )annotationsN)AnyDictListMappingSequencebackground_share_plan)seedplateinterior)
backgroundprev   z1.202607190212z2.202609051240)12ref_planprev_anchor_tagrationale_koc                J   i }| xs i j                  d      }t        |t              s|S |j                         D ]a  \  }}t        |t              s|D cg c]  }|t        vs| }}|s2|D ]  }|j                  |d        t        |      |t        |      <   c |S c c}w )u  `shot_plans` 항목에서 **모르는 칸을 떼어 낸다**. 뗀 것을 돌려준다.

    ★조용히 버리지 않는다 — 무엇을 뗐는지 호출자가 받아 기록한다. 그래야
     「모델이 자꾸 무엇을 더 넣는가」를 나중에 되짚을 수 있다.

    ★샷과 그룹의 연결은 `share_groups[].shot_tags` 가 이미 표현한다. 그래서
     샷마다 `group_key` 를 또 적는 것은 **중복이지 정보가 아니다** — 떼어도
     잃는 것이 없다.
    
shot_plansN)get
isinstancedictitemsSHOT_PLAN_FIELDSpopsortedstr)resultdroppedplanstagentrykextras          `/Users/manta/Documents/Projects/TheRoad-I1/backend/app/modules/pipeline/background_share_plan.pynormalize_shot_plansr(   2   s     %'G\r|,EeT"kkm
U%&!?EqQ.>%>E?AIIa "5MC $ N @s   B #B c                @    | t         vrt        d|        t         |    S )Nu2   background_share_plan 프롬프트 버전 없음: )PROMPT_VERSION_MAP
ValueErrorversions    r'   resolve_prompt_versionr.   L   s)    ((MgYWXXg&&    c                     ddddddidddd	d
dgdd} ddddddddddt        t              dddddidddi| dg ddddddt        t              dddiddiddgdddddgdS )Narray   objectFtypeintegerstring)r4   	minLength)scene_indexquote_kor8   r9   )r4   additionalProperties
propertiesrequired)r4   minItemsr   )r4   enum)	group_keybg_authority	shot_tagsr   evidence)r?   r@   rA   rB   r   r   )r4   r;   r<   )r4   r:   )share_groupsr   rC   r   )listBG_AUTHORITIES	REF_PLANS)rB   s    r'   build_share_schemarG   R   s    Qe &	2%-A> '
3

H %  Q$e.6Q%G1915n1E)G.5106/A&C)/(:$,#!-$ ! %-5-1)_%>,2H+=)/(:	# ",#)%(
R $\2W, ,r/   c                    t        j                  dt        |             }|r4t        |j	                  d            t        |j	                  d            fS dS )NzS(\d+)sh(\d+)$r2   r   ) ʚ;rI   )rematchr   intgroup)r#   ms     r'   _tag_keyrO      sA    
"CH-A12C
OS_-FFr/   c                R    dj                  t        |       j                               S )N )joinr   split)texts    r'   _norm_wsrU      s    88CIOO%&&r/   c                   g }| j                  d      xs g }| j                  d      xs i }t        |      }i }i }t               }	|D ]  }
t        |
xs i j                  d      xs d      }|r*||	v r|j	                  d| d       |	j                  |       |
xs i j                  d      t        vr|j	                  d| d	       t        |
xs i j                  d
      xs g       }|D ].  }||v r|j	                  d| d||    d| d       |||<   |||<   0 |t        |t              k7  r|j	                  d| d       |
xs i j                  d      xs g D ]v  }|xs i j                  d      }t        |xs i j                  d      xs d      }t        |j                  |      xs d      }|r||vs_|j	                  d| d| d       x  |D cg c]	  }||vs| }}|r|j	                  d|dd         |D cg c]  }|t        |      vs| }}|r|j	                  d|dd         i }|D ]K  }
t        |
xs i j                  d
      xs g       }|s(|d   |t        |
j                  d      xs d      <   M |D ]  }|j                  |      }t        |t              s|j	                  d| d       ;|j                  d      }|t        vr|j	                  d| d|       l|j                  |      }|dk(  r|j                  d      }|s|j	                  d| d       |j                  |      |k7  r|j	                  d| d | d!       t        |      t        |      k\  s|j	                  d| d | d"        |j                         D ]G  \  }}|j                  |      xs i }|j                  d      dk7  s0|j	                  d| d#| d$       I |S c c}w c c}w )%u  결정론 완전성 검증 — 위반 리스트 반환 (fail-closed 재시도용).

    v2 (2026-07-21 E2E10 Codex 재리뷰): group_key 유일성 fail-closed —
    소비부(스틸 groupbg record key·group_sig·group_of 색인, first_of_group
    검증)가 group_key 를 실질 PK 로 쓰므로 중복 키는 서로 다른 장소
    그룹을 한 groupbg/sidecar 로 합치고 첫 샷/prev 검증을 약화시킨다.
    rC   r   r?    zgroup_key 'u+   ' 중복 — 그룹 키는 유일해야 함r@   u   그룹 'u   ' bg_authority 무효rA   u   샷 u    그룹 중복 배정(   ·))keyu'   ' shot_tags 가 스토리 순이 아님rB   r8   r9   u   ' 인용 원문 부재 (scene u   샷 커버리지 누락: N   u   미선택 샷 배정: r   u    shot_plan 누락r   u    ref_plan 무효: r   r   u    prev 앵커 누락u    prev 앵커 u    가 다른 그룹u    가 앞 샷이 아님u
   ' 첫 샷 u    은 background 여야 함)r   rD   setr   appendaddrE   r   rO   rU   r   r   rF   r   )r    rA   scene_texts
violationsgroupsr"   expectedseengroup_ofgroup_keys_seenggktagstevsiquotesrcmissingr&   first_of_groupprpanchorfirsts                            r'   validate_share_planrt      sD    JZZ'-2FJJ|$*EIHD!H5O!'r{+1r2_$!!!"%PQ #G==(>,ABCQW"MM+.4"5ADy!!D+A$q'"RDPQ"RSDGHQK	  6$H--,STU7--
+1r1B(.BbhB^^J7=2>E;??2.4"5CE,!!rd"@AFH 2) 6 #4(Qatmq(G45gbqk]CD71X 6QE725!9+>?%'NQW"MM+.4"5<@GN3quu[17R89  IIaL!T"QC'89:UU:YQC'9"@A\\!_()QC':;<<<2%QC}VH<NOPf!,QC}VH<RST) * $))+	EIIe"55,2$j/IJL ,
 M 5 8s   !	O<+O<P!Pr      )project_configprompt_versionmax_attemptscall_structured_fnopik_metadatac        	           ddl m}	 ddlm}
 |ddlm} |}t        |      } |
t        d|      } |
t        d||| 	      }t               }g }t        d
|d
z         D ]  }|s|n|dz   dj                  d |D              z   }ddlm} 	  |t        ||||t        |      }t!        |      }|rUt        j                  dt#        |      t%        t'        |j)                               dd              |j+                  d|       t-        |||      }|s||dc S t        j                  d||dd          |	dd|dd  d      # |$ r:}d| g}t        j                  d|t        |      dd        ||k\  r Y d}~"d}~ww xY w)u   계획 저작+검증 재시도 — 소진=AppError (fail-closed, lane_plan 관례).

    반환 {"plan": result, "attempts": n}.
    r   )AppError)load_promptN)call_structuredsystemr,   user_template)r-   scenes_blockshots_blockr2   z&

PRIOR ATTEMPT VIOLATIONS (fix all):

c              3  &   K   | ]	  }d |   yw)z- N ).0vs     r'   	<genexpr>z,run_background_share_plan.<locals>.<genexpr>  s     5*Q"QC*s   )SchemaValidationError)rv   schema_namerz   zschema: uI   background_share_plan: 스키마 위반(attempt %d) — 다시 쓴다: %s   uC   background_share_plan: shot_plans 곁가지 칸 제거 %d건 — %s   _dropped_shot_plan_fields)planattemptsu4   background_share_plan: 검증 위반(attempt %d): %s   z-step.contract_violation.background_share_planu7   background_share_plan 검증 실패(재시도 소진): i  )codemessagestatus_code)app.core.errorsr|   app.modules.prompt_loaderr}   app.modules.llm.llm_clientr~   r.   _MODULErG   rangerR   app.modules.llm.safetyr   loggerwarningr   r(   lenr   rD   r   
setdefaultrt   )r   r   rA   r_   rv   rw   rx   ry   rz   r|   r}   r~   resolvedr   userschemar`   attemptcontentr   r    exc_droppeds                          r'   run_background_share_planr      s     )5!>,%n5H(H=F(!{D  !FJL1,-($>>ii5*556 	 	A	'&-7+F (/NNUHtD)9$:2A$>?A 98D(KH
"88BZ^	
M .Z <EjQSRSnEUV 7 % 	$SE*+JNN[S$3) ,&	s   	EF
/E??F)r    Dict[str, Any]returnzDict[str, List[str]])r-   r   r   r   )r   r   )r#   r   )rT   r   r   r   )r    zMapping[str, Any]rA   Sequence[str]r_   Mapping[int, str]r   z	List[str])r   r   r   r   rA   r   r_   r   rv   Dict[str, Any] | Nonerw   r   rx   rL   rz   r   r   r   )__doc__
__future__r   loggingrJ   typingr   r   r   r   r   	getLogger__name__r   r   rE   rF   VALIDATION_VERSIONr*   r   r(   r.   rG   rO   rU   rt   r   r   r/   r'   <module>r      s!   #  	 5 5			8	$
!."	   
 
  C 4'8vG
'UUU #U 	U| -1+/SS S 	S
 #S *S S S )S Sr/   