
    QujPb              	          d Z ddlZddlZddlZddlmZmZ ddlm	Z
 ddlmZmZmZmZ ddlmZ ddlmZmZ ddlmZ dd	lmZ dd
lmZ ddlmZ ddlmZmZ  ej@                  e!      Z"dZ# ejH                  d      Z% ejH                  d      Z&dee   de'e(   de)ee(   ee   f   fdZ*deee(ef      deee(ef      de(ddfdZ+ G d de      Z,y)u  Shot description 검증·재작성 단계.

shot_extract 직후 실행. 각 shot의 description이 '한 찰나' 원칙을 지키는지
LLM으로 검증하고, 시간 연결어/연속 동작이 섞인 경우 재작성한다. 원본은
`original_description` 필드로 백업.

체크포인트 구조는 shot_extract와 동일해서 다운스트림 step들은 로더 경로만
`shot_validator`로 교체하면 validated 버전을 읽는다.
    N)ThreadPoolExecutoras_completed)Path)AnyDictListOptional)settings)_load_cp_parse_traits)AppError)
StepRunner)EntityCanon)call_structured)load_promptload_schema   z^C\d+$z^(C\d+)O\d+$raw	pool_keysreturnc                    g }g }t               }| D ]  }||dk(  r|j                  |       t        |      }t        j	                  |      }|r|j                  d      n|}t        j	                  |      s|j                  |       x|r||vr|j                  |       ||vs|j                  |       |j                  |        ||fS )u  character_ids LLM 출력 strict 검증.

    각 entry 를 다음 룰로 평가:
      1. None / 빈 문자열 — drop
      2. composite C##O## → base C## 로 collapse 후 재검증
      3. ^C\d+$ 정규식 미일치 — drop (silent corruption 차단; '42', 'None', 'C-A' 등)
      4. character_pool 이 비어있지 않은데 그 안에 없음 — drop (stale/hallucinated id)
      5. 통과 entry 는 base form 으로 dedupe 보존 (입력 순서)

    Returns: (accepted base ids, rejected raw entries — log 용).
        )setappendstr_COMPOSITE_CHAR_ID_RE	fullmatchgroup_CHAR_SHORT_ID_REadd)	r   r   acceptedrejectedseenentryscomposite_matchbases	            X/Users/manta/Documents/Projects/TheRoad-I1/backend/app/core/steps/shot_validator_step.py_filter_character_idsr*   #   s     HHUD=ERKOOE"J 099!<+:$$Q' **40OOE"Y.OOE"tHHTNOOD!' ( X    shot_validator_cpproject_configconsumer_stepc                   | sy| j                  d      xs i j                  d      xs g }t        d |D              }|sy|xs i }|j                  d      du rt        j                  d||       yt	        d| d	| d
d      )u  G4.6 RO-15 — shot_validator manifest 의 validator_status='failed' 씬 fail-fast.

    **계층 정책** (G4.6 Phase 3 fix iter 3):
      1차 (gate): step_runner.check_gate 가 dep partial + manifest
        allow_partial_downstream=False 차단. project_config 의
        partial_override_config_key (=allow_failed_validator) 가 ``is True``
        (strict bool) 인 경우 통과.
      2차 (consumer guard, 본 helper): force / resume / 비표준 dispatch case 에
        gate 통과 후 도달 시 second line of defense. 동일 키 + 동일 strict 정책.

    **guard 적용 consumer** (description / characters / shot text 직접 사용 —
    failed 씬의 stale 값을 LLM input 으로 사용하면 잘못된 결과):
      - shot_selection_step / shot_dependency_step / shot_director_step
      - shot_dependency_t2i_step / scene_context_loader
      - entity_all_character (entity_steps) / scene_camera_flow_step
      - shot_staging_step / background_master_plan_step

    **guard 미적용 consumer** (cascade dependencies — gate 차단으로 dispatch
    자체 안 됨, 또는 selected_shot_indices 만 사용해서 stale shot text 무관):
      - background_classify / background_planner / background_chain_planning
      - background_prompt / floor_plan_prompt
    Ndatascenesc              3      K   | ]J  }t        |t              r8|j                  d       dk(  r$|j                  d      |j                  d       L yw)validator_statusfailedscene_indexN)
isinstancedictget).0scs     r)   	<genexpr>z*assert_no_failed_scenes.<locals>.<genexpr>k   sL      Bb$FF%&(2FF=!-	 	}s   AAallow_failed_validatorTum   %s: upstream shot_validator failed for scenes %s; allow_failed_validator=True — explicit operator override.zstep.upstream_validator_failedz+ blocked: shot_validator failed for scenes z_. Re-run shot_validator first or set allow_failed_validator=true as explicit operator override.  codemessagestatus_code)r8   sortedloggerwarningr   )r,   r-   r.   r1   r4   cfgs         r)   assert_no_failed_scenesrF   L   s    8 ##F+1r66x@FBF  F 

BC
ww'(D0J6	

 	
-oH Ql m  r+   c                      e Zd ZdZdefdZdedeeeef      fdZ	ddedeeef   fdZ
	 dd	eeef   d
edededeeef   deeeef      deeeef   ef   fdZdeeef   fdZdddeeeeee   f   f   deeeef      deeef   fdZdeeeeee   f   f   fdZedeeeeee   f   f   dee   defd       Zy)ShotValidatorStepuH   각 shot description을 '한 찰나' 원칙에 맞게 검증·재작성.r   c                 ^   ddl }ddl}ddlm} t	        dd| j
                        xs d}|j                  |j                  d            j                         dd	  || j                        d
}|j                   |j                  |d      j                  d            j                         S )um  프롬프트 팩 실효 버전을 지문에 싣는다 (2026-08-07).

        이 스텝은 팩을 **버전 미지정**으로 로드해 항상 최신을 쓴다. 그런데
        지문에는 팩이 없어서, 팩을 고쳐도 완료된 체크포인트가 그대로
        재사용됐다 — 이번에 실측한 "만들어 놓고 안 나간다" 결함과 같은
        형태다. 팩이 바뀌면 재실행되게 한다.

        ★재실행 비용이 크다: 이 스텝의 산출(샷 description)은 하류
        거의 전부의 입력이다. 팩을 올릴 때는 재실행 범위를 함께 판단할 것.
        r   N)compute_config_hashshot_validatorsystemdbr   utf-8   )
prompt_shaproject_config_hashT)	sort_keys)hashlibjsonapp.core.step_runnerrJ   r   rN   sha256encode	hexdigestr-   dumps)selfrT   _jsonrJ   textpayloads         r)   _config_hashzShotValidatorStep._config_hash   s     	<
 +X$''BHb!..G$&&/ik#27#6t7J7J#K

 ~~EKK4077@

)+	r+   step_idc                     t        t        j                        | j                  z  dz  dz  | j                  z  |z  dz  }|j                         r%t        j                  |j                  d            S y )Ncheckpointsepisodeszmanifest.jsonrO   )encoding)	_Pathr
   projects_dir
project_id
episode_idexistsr\   loads	read_text)r[   r`   cps      r)   _load_prev_checkpointz'ShotValidatorStep._load_prev_checkpoint   s}    (''(oo  oo	
   	 99;;;r||W|=>>r+   modec                 t   | j                  d      }|r!|j                  di       j                  d      st        ddd      | j                  d      }i }|r~|j                  di       }|j                  d	      xs |j                  d      xs g }|D ]?  }|j                  d
      |j                  dd      xs |j                  dd      |<   A t        dd| j                        }t        dd| j                        }	|d   d   }
| j                         }| j                  ||      }t        |
      D cg c]  \  }}|j                  d
      | }}}|rt        ddt        |       dd      g }d}d}d}t        t              5 }|
D ci c]q  }|j                  | j                  ||j                  |j                  d
      d      |j                  |j                  d
      d      ||	|      |j                  d
      s }}t        |      D ]O  }||   	 |j                         \  }}|j-                  |       ||z  }|t        |j                  d g             z  }Q 	 d d d        |j1                  d# $       t"        j3                  d%t        |      |||       t        |      |z
  t        |
      || j5                         |||d&d'S c c}}w c c}w # t         $ r}t"        j%                  d|       |dz  }t'        fd|
D        d       }|et)        |      }d|d<   t+        |      d d |d<   |j                  d       xs g D cg c]
  }i |dd!i nc c}w c}|d <   |j-                  |       nt"        j/                  d"       Y d }~d }~ww xY w# 1 sw Y   >xY w)(Nshot_extractr0   r1   zstep.no_shotsuL   shot_extract 결과가 없습니다. 먼저 shot_extract를 실행하세요.r=   r>   
scene_savesegmentsr5   r]   r   segmentrK   rL   rM   validator_schemashot_cpzstep.invalid_datau.   shot_extract 결과에 scene_index 없는 씬 u!   개 — upstream 데이터 손상i  r   )max_workersu&   shot_validator: scene %s failed — %sr   c              3   L   K   | ]  }|j                  d       k(  s|  yw)r5   Nr8   )r9   xsis     r)   r;   z-ShotValidatorStep._execute.<locals>.<genexpr>   s#      WLqAEE-<PTV<VLs   $$r4   r3      validator_failure_reasonshotsfailed_carry_originaluA   shot_validator: scene %s not found in original input — skippingc                 &    | j                  dd      S )Nr5   r   ry   )r&   s    r)   <lambda>z,ShotValidatorStep._execute.<locals>.<lambda>  s    155#:r+   )keyz@shot_validator: %d scenes, %d shots total, %d revised, %d failed)r1   total_shotschanged_shots)completed_countapplicable_countfailed_countconfig_hashr0   )rm   r8   r   r   rN   r   _load_character_pool_build_entity_blocks	enumeratelenr   MAX_WORKERSsubmit_validate_scener   result	ExceptionrC   rD   nextr7   r   r   errorsortinfor_   )r[   rn   rv   save_cpscene_texts	save_datasegsr&   rL   schemascenes_inputcharacter_poolentity_blocksir:   missingresultschanged_totalshots_totalfailed_scenespoolfuturesfut	out_scenechangedexcorigmarkedshr{   s                                @r)   _executezShotValidatorStep._execute   s   ,,^<gkk&"599(C$f  ,,\:&(FB/I==,M	h0GM2DUU=)>&'eeFB&7&O155B;OKO 
 -xDGGD-/AdggNvx022411.'1R"+L"9["9BRVVM=R=Z1"9[(HWVwx  )+K8D ' 'B ((OOBFF=$92>!%%bff]&;R@" 66-() '   $G,S\),&Iw0 y)(s9=="#=>>; - 9X 	:;NL	
  #7|m; #L 1)  ,,.!*!.
 	
C \  ! NN#KRQTU!Q&M
   WL WY]^D'!%d5=12=@Xds^9: (,xx'8'>B'>+'> PrO#57NO'>+w  v._ -# 98sb    KKN-A6K#N-'K(:6N-#N-(	N*1A+N%M,
+4N%N-%N**N--N7Noriginal_scene
scene_textentity_blockrL   r   r   c                   " |rt        |j                               n	t               }|j                  dg       }|j                  d      }	|s|dfS dj                  d |D              }
d|	 d| d| d	|
 d
	}i | j                  di | j                  j                  di       ddii}d| j                  fd| j                  fd|fg}d}d}d}|D ]  \  }}|dk(  r|rt
        j                  d|	       $	 t        d||||d|	 d| | j                  |gd|	i            }|r|j                  d      st        d      |}|dk7  rt
        j                  d|	|        n ||J ||j                  dg       D ci c]  }|j                  d      | }}g }d}|D ]]  }|j                  |j                  d            }t        |      }|r|j                  d      rk|j                  d d!      j!                         }|rI||j                  d"d!      k7  r4|j                  d"d!      |d#<   ||d"<   |j                  d$d!      |d%<   |d&z  }|j                  d'      }t#        |t$              r>t'        ||      \  }}||d'<   |r(t
        j                  d(|	|j                  d      ||       |j                  d)      } t#        | t$              r| D !cg c]  }!|!t        |!       c}!|d)<   |j)                  |       ` i |d|i|fS # t        $ rY}|}t        |      j                         "t        "fddD              r|dk(  rd}t
        j                  d|	||       Y d}~d}~ww xY wc c}w c c}!w )*u  단일 씬의 shots 검증. (결과 씬, 변경된 shot 수) 반환.

        entity_block: 사전 빌드된 [Entity map for this scene] 문자열 (또는 빈 문자열).
        character_pool: {short_id: ...} — character_ids 검증 시 candidate set.
        r~   r5   r   
c              3   l   K   | ],  }d |j                  dd       d|j                  dd        . yw)zShot 
shot_index?: descriptionr   Nry   )r9   r   s     r)   r;   z4ShotValidatorStep._validate_scene.<locals>.<genexpr>;  s=       
 BFF<-.br1J0KLs   24u   [씬 z]


u   [Shots 검증 대상]
u   

각 shot의 description이 '한 찰나' 원칙에 맞는지 검증하고, 위반 시 재작성하세요. character_ids 는 entity map 의 short_id 사용.rK   modelgptprimary
retry_samefallback_gptNFzMshot_validator: scene %s skipping retry_same (deterministic failure detected)shot_validator_s_)
extra_tagsextra_metadata)stepsystem_promptuser_promptresponse_schemar-   schema_nameopik_metadatazempty response (no shots)z(shot_validator: scene %s succeeded on %sc              3   &   K   | ]  }|v  
 y wN )r9   kmsgs     r)   r;   z4ShotValidatorStep._validate_scene.<locals>.<genexpr>|  s     x)wAqCx)ws   )content_filterzcontent filtersafety
recitationzempty responseTz'shot_validator: scene %s %s failed (%s)r   r   revised_descriptionr   r   original_descriptionreasonvalidator_reasonr   character_idsu_   shot_validator: scene %s shot %s — dropping malformed/out-of-pool character_ids %s (kept %s).
characters)r   keysr8   joinr-   rC   r   r   build_opik_metadata
ValueErrorr   r   loweranyrD   r7   stripr6   listr*   r   )#r[   r   r   r   rL   r   r   r   r~   r{   shots_blockr   
gpt_configattemptsreslast_excskip_retry_sameattempt_labelrE   	candidater   r	revisions	new_shotsr   r   revnew_shrevisedrev_char_idsr"   r#   	rev_charsrz   r   s#                                     @r)   r   z!ShotValidatorStep._validate_scene&  s    3AC++-.ce	""7B/.!1$$ii  
 
 

 B4sl$n%k] 3_` 	
!!
 %%))*:B?

 ++,4../Z(

 (,"*M3,c +)"( +$*#&"22$a G"&":":}ogtvxfy":"z	 !	g(>$%@AA I-KKBM 3 #+L ;'''N58WWWb5IJ5IQUU<(!+5I	J*,	B--| 45C"XF779%!gg&;R@FFHG7bff]B.G#G9;r9R5607}-58WWXr5J121  #ww7lD1)>|Y)W&Hh.6F?+:| 4h
  GGL1	i.<E+WIqCFI+WF<( V$A D 6.5'95w>>m  #hnn& x)wxx$	1*.=s   K< ,Xs,   5A(L<M-M2M2	M*AM%%M*c                 @    | j                  | j                               S )u   Test-only wrapper — pool 을 자체 로드해서 빌드.

        production path 에서는 _execute() 가 pool 을 한 번 로드 후
        _build_entity_blocks(pool, shot_cp=...) 직접 호출.
        )r   r   )r[   s    r)   #_build_entity_blocks_for_all_scenesz5ShotValidatorStep._build_entity_blocks_for_all_scenes  s     (()B)B)DEEr+   ru   r   rv   c          	      $   |st         j                  d       i S t        | j                  | j                  d      }|st         j                  d       |xs | j                  d      }|si S | j                  |t        |j                                     }|j                  d      xs i j                  dg       xs g D ci c]  }|j                  d      |d   | c}S |j                  d      xs i j                  dg       xs g }i }|D ]v  }|j                  d      }	|	t        d |j                  d	      xs g D        D 
ch c]  }
|
j                  d
      r|
|v r|
 c}
      }|sb| j                  ||      ||	<   x |S c c}w c c}
w )u%  [Entity map for this scene] 사전 빌드 — character (C##) 만.

        Source 우선순위 (multi-source — DAG order 7.25 가 entity_merge/scene_director
        보다 빨라 first-run 에서 RC-F 가 부분 비활성화 — force re-run 시 활성화):

        1. **Primary character pool**: entity_merge cp 의 characters → {short_id, name, [stable_traits]}.
           부재 시 EntityCanon DB fallback. 둘 다 부재면 RC-F 비활성 → WARNING + {}.
        2. **씬별 좁힘 (refinement)**: scene_director.present_entity_ids 의 C## 만.
           부재 시 모든 씬에 universal pool block 적용.

        pool 인자는 caller 가 _load_character_pool() 결과를 한번만 로드해서 전달
        (ThreadPoolExecutor 병렬 path 에서도 동일 pool 재사용).
        u   shot_validator: entity character pool unavailable (entity_merge + EntityCanon both empty) — RC-F entity mapping inactive this run; re-run after entity pipeline produces characters.scene_directorus   shot_validator: scene_director cp absent — applying universal entity pool to all scenes (no per-scene narrowing).rp   r0   r1   r5   c              3   H   K   | ]  }|r|j                  d       d     yw)Or   N)split)r9   sids     r)   r;   z9ShotValidatorStep._build_entity_blocks.<locals>.<genexpr>  s(        B IIcN1% Bs    "present_entity_idsC)rC   rD   r   rg   rh   r   rm   _format_entity_blockrB   r   r8   
startswith)r[   r   rv   director_cpcp_for_indices	universalr:   director_scenesblocksr{   bidscene_char_idss               r)   r   z&ShotValidatorStep._build_entity_blocks  s   & NNW
 I tAQRKKF %R(B(B>(RN!	11$tyy{8KLI *--f5;@@2NTRTTTB66-(4 =!9,T  '??628b==hKQr!#!B&Bz $  "'; < B B % 
 >>#&3$;   % N "224HF2J! "$ 7%s   ?FF
c                 ,   t        | j                  | j                  d      }|r|j                  d      xs i j                  d      xs g }i }|D ]  }t	        |t
              s|j                  d      }t	        |t              r|j                  d      sG|j                  d      xs |}t        |j                  d            dd	 }t        |      |f||<    |r|S 	 | j                  j                  t        j                  t        j                  t        j                        j                  t        j                  | j                  k(  t        j                   d
k(        j#                         }|D 
ci c]f  }
t	        |
j                  t              rJ|
j                  j                  d      r/|
j                  |
j                  t        |
j                        dd	 fh c}
S # t$        $ r"}	t&        j)                  d|	       i cY d}	~	S d}	~	ww xY wc c}
w )u   character pool — entity_merge cp 우선, 부재 시 EntityCanon DB.

        반환: {short_id: (name, stable_traits[:2])} — character (C##) 만.
        entity_merger0   r   short_idr   namestable_traitsN   	characteruY   shot_validator: EntityCanon character query failed (%s) — RC-F pool empty for this run.)r   rg   rh   r8   r6   r7   r   r   r   rN   queryr   r  r  r  filterentity_typeallr   rC   rD   )r[   merge_cpcharsr   cr   r  traitsrowsr   r   s              r)   r   z&ShotValidatorStep._load_character_pool  s    DOOT__nM\\&)/R44\BHbE57D!!T*eeJ'!#s+3>>#3FuuV}+&quu_'=>rB Y/S	  	77==$$  )) f&&$//9'';6 ce   
!**c*qzz/D/DS/I JJq!?!CDD
 	
  	NN014 I	
s&    BG# 5A+H#	H,H	H	H
sorted_idsc                     dg}|D ];  }| |   \  }}|rddj                  |       nd}|j                  d| d| |        = dj                  |      dz   S )	uB   [Entity map for this scene] 문자열 포맷 — 표시용 helper.z[Entity map for this scene]u    — z, r   z- r   r   r   )r   r   )r   r  linesr   r  r  
traits_strs          r)   r   z&ShotValidatorStep._format_entity_block&  sp     //C9LD&8>56!2 34BJLL2cU"TF:,78  yy&((r+   )resumer   )__name__
__module____qualname____doc__r   r_   r	   r   r   rm   r   tupleintr   r   r   r   r   staticmethodr   r   r+   r)   rH   rH      s   Rc :S Xd38n5M o
S o
S#X o
p 48G?S#XG? G? 	G?
 G? S#XG? !c3h0G? 
tCH~s"	#G?RFT#s(^ F -1	A3c49n--.A $sCx.)	A
 
c3hAF*
d3c49n0E+E&F *
X 
)3c49n--.
)I
) 

) 
)r+   rH   )-r  rU   r\   loggingreconcurrent.futuresr   r   pathlibr   re   typingr   r   r   r	   app.core.configr
   app.core.entity_protectionr   r   app.core.errorsr   rV   r   app.models.projectr   app.modules.llm.llm_clientr   app.modules.prompt_loaderr   r   	getLoggerr  rC   r   compiler    r   r   r   r  r*   rF   rH   r   r+   r)   <module>r*     s      	 ? ! , , $ > $ + * 6 >			8	$ BJJy) "

?3 &	c&3x& 49d3i &R8S#X/8T#s(^,8 	8
 
8vj)
 j)r+   