
    jW                        d Z ddlZddl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
mZmZmZmZ ddlmZmZ ddlmZmZ  ej*                  e      ZdZded	efd
Zded	eee
f   fdZd	efdZdddiddidddgddZdddddddiddddiddiddiddiddiddiddiddideddeddeddg dddZdddiddiddidddidddiddddidddddd gd!dddiddddgid"g d"ddidgddgidddidd#dd$iid#gddgid%d&d'gddd(g d(ddZ G d) d*      Z 	 	 	 d0d+ed,ee   d-ee   d.eeeee
f         d	eee
f   f
d/Z!y)1u[  요소 추출 v3 — LiteLLM 기반 독립 4단계.

모든 LLM 호출이 독립적 (멀티턴 세션 없음).
이전 단계 결과는 프롬프트에 인라인으로 포함.

Step 1: 스타일 + 요소 이름 (gemini-pro)
Step 2a: GPT 리뷰/필터링 (gpt)
Step 2b: GPT 상세 추출 (gpt)
Step 3: T2I 프롬프트 생성 (gemini-flash, 병렬)
    N)ThreadPoolExecutoras_completed)AnyDictListOptionalTuple)call_structuredPIPELINE_STEPS)load_promptload_schemaentity_extractor_v2namereturnc                 $    t        t        | fi |S N)r   _MODULE)r   kwargss     ^/Users/manta/Documents/Projects/TheRoad-I1/backend/app/modules/pipeline/entity_extractor_v3.py_load_promptr      s    w///    c                 l    | j                  d      r| j                  dd      n| }t        t        |      S )N.json )endswithreplacer   r   )r   stems     r   _load_schemar       s,    (,g(>4<<$DDw%%r   c                      t        d      S )Nsystem)r    r   r   _load_systemr"   %   s    !!r   objecttypestringinteger)r   appearancesr'   F)r$   
propertiesrequiredadditionalPropertiesu+   에피소드 상세 요약 (5문장 이상))r$   descriptionarrayug   인물의 물리적 존재 판단과 씬 간 시각적 일관성에 필요한 핵심 규칙만 (2~5개))r$   itemsr+   )r$   r-   )episode_summaryvisual_world_ruleseraregiongenrebuilding_styleclothing_stylevehicle_style
color_tone
must_avoid
characters	locationspropsanyOfnullspace_profilesingle_spacemulti_space)r$   enum)kindallowed_space_keysdefault_space_keyreference_requiredboolean)locationvisual_identityrF   rG   )r   entity_typer+   visual_traits
t2i_promptmetadata_jsonc                   ^    e Zd Zdee   defdZddefdZedee   fd       Zd	efd
Z	d Z
y)_Checkpointcheckpoint_dirfulltextc                 l   d | _         i | _        |rt        j                  |d       t	        j
                  |j                  d            j                         d d }t        j                  j                  |d| d      | _         t        j                  j                  | j                         rn	 t        j                  t        | j                   d      j                               | _        t        j!                  d| j                  j#                  d	             y y y # t$        $ r
 i | _        Y y w xY w)
NT)exist_okutf-8   entity_extraction_r   encodingz!Entity checkpoint loaded: step=%scompleted_step)_path_dataosmakedirshashlibmd5encode	hexdigestpathjoinexistsjsonloadsopenreadloggerinfoget	Exception)selfrN   rO   ep_hashs       r   __init__z_Checkpoint.__init__   s    

KK6kk(//'":;EEGKGn8J7)SX6YZDJww~~djj)$!%Dg,N,S,S,U!VDJKK CTZZ^^TdEef *	  ! $!#DJ$s   1A,D   D32D3Nkeyc                 :    | j                   j                  ||      S r   rY   ri   )rk   rn   defaults      r   ri   z_Checkpoint.get   s    zz~~c7++r   r   c                 8    | j                   j                  d      S )NrW   rp   rk   s    r   rW   z_Checkpoint.completed_step   s    zz~~.//r   stepc                 \   | j                   sy | j                  j                  |       || j                  d<   | j                   dz   }t        |dd      5 }t	        j
                  | j                  |dd       d d d        t        j                  || j                          y # 1 sw Y   *xY w)	NrW   z.tmpwrR   rU   F   ensure_asciiindent)rX   rY   updatere   rc   dumprZ   r   )rk   rt   r   tmpfs        r   savez_Checkpoint.save   s    zz

&!'+

#$jj6!#sW-IIdjj!%B .


3

# .-s   $B""B+c                     | j                   r_t        j                  j                  | j                         r5t        j                  | j                          t
        j                  d       y y y )NzEntity checkpoint deleted)rX   rZ   r`   rb   unlinkrg   rh   rs   s    r   deletez_Checkpoint.delete   s@    ::"''..4IIdjj!KK34 5:r   r   )__name__
__module____qualname__r   strrm   ri   propertyrW   r   r   r!   r   r   rM   rM      sU    $x} $ $,s , 0 0 0$ $5r   rM   rO   rN   project_llm_configprior_entitiesc                   45 t        ||       }t               }d}|rdt        j                  |dd      z   dz   }|j                  r6|j                  dk\  r'|j                  d      }t        j                  d	       n_t        j                  d
       t        d||       }|dt        d||       z   z  }t        d||t        d      }|j                  d|       |j                  dd      |j                  dd      |j                  dd      |j                  dd      |j                  dd      |j                  dd      |j                  dd      |j                  dd      |j                  dd      d	}	t        j                  dt        |j                  dg             t        |j                  dg             t        |j                  d g             |	d   |	d   |	d          |j                  r6|j                  d!k\  r'|j                  d"      }
t        j                  d#       nt        j                  d$       d%d&lm} d' }t        d(      } |d)|  ||j                  dg              ||j                  dg              ||j                  d g             *      }	 t        d+d,||d+      }
|j                  d!||
0       |
j                  d.g       }|D ch c]C  }t%        |j                  d1d2            d3k  r#t%        |j                  d4d%            d5k  r|d6   E }}|r t        j                  d7t        |      |       g }|j                  dg       D ]   }|d6   |vs|j'                  |d6   d8f       " |j                  dg       D ]   }|d6   |vs|j'                  |d6   d9f       " |j                  d g       D ]   }|d6   |vs|j'                  |d6   d:f       " |j                  rB|j                  d;k\  r3|j                  d<i       4t        j                  d=t        4             nt        j                  d>t        |             i 4d?j)                  d@ |D              }	 t        dA      }t        dB|| C      }t        d+dD||dE      }|j                  d.g       D ]-  }|j                  dFd      |j                  dGg       dH4|d6   <   / t        j                  dIt        4      t        |             |D cg c]  \  }}|4vs||f }}}|rt        j#                  dJt        |             d?j)                  dK |D              }t        dB|| C      }	 t        d+dD||dL      }|j                  d.g       D ]-  }|j                  dFd      |j                  dGg       dH4|d6   <   / 	 |j                  d;||
4O       g } g }!g }"t        |      }#|#d%k(  rt        j                  dP       nUt               5dQt$        dRt*        dSt*        dTt,        t$        t*        t*        t.        t*        t0        f   f   f45fdU}$|j                  dVi       }%t3        |      D &cg c]  \  }&\  }}||%vs|&||f }'}}&}|%j5                         D ]V  \  }(})|)j                  dWd8      }*|*d8k(  r| j'                  |)       /|*d9k(  r|!j'                  |)       F|"j'                  |)       X |'r@t7        d3t        |'            }+t        j                  dX|#t        |%      |+       g },t9        |+Y      5 }-i }.|'D ]4  \  }&}(}*|.rt;        j<                  d       |&|.|-j?                  |$|&|(|*      <   6 tA        |.      D ]J  }/|/jC                         }0|,j'                  |0       |0\  }1}(}*})|*|)dW<   |)|%|(<   |j                  dZ||
4|%[       L 	 d/d/d/       |,jE                  d\ ]       |,D ]F  \  }2}3}*})|*d8k(  r| j'                  |)       |*d9k(  r|!j'                  |)       6|"j'                  |)       H nt        j                  d^t        |%             t        j                  d_t        |       t        |!      t        |"             |jG                          | |!|"|	d`S # t         $ r%}t        j#                  d-|       d.g i}
Y d/}~d/}~ww xY wc c}w c c}}w # t         $ r!}t        j#                  dM|       Y d/}~Bd/}~ww xY w# t         $ r!}t        j#                  dN|       Y d/}~od/}~ww xY wc c}}}&w # 1 sw Y   oxY w)au  독립 4단계 요소 추출.

    .. deprecated::
        v4 파이프라인 (entity_steps.py / entity_extractor_v4) 도입 후 모든
        production 호출자가 이전됨. 이 legacy public function 은 여전히
        ENTITY_DETAIL_SCHEMA 의 description / visual_traits / t2i_prompt 를
        LLM 출력 그대로 사용하므로 entity_t2i 의 unsourced trait 차단 정책
        (entity_steps.py 의 source-detail forward) 가 적용 안 됨. 신규 호출자
        는 v4 경로 사용. 본 function 은 unit test 외 호출 금지 — production
        re-introduction 시 동일 forward 정책 또는 fail-fast 를 먼저 도입할 것.

    Args:
        fulltext: 시나리오 전문
        checkpoint_dir: 체크포인트 저장 경로
        project_llm_config: 프로젝트별 LLM 모델 설정
        prior_entities: 이전 에피소드 기존 요소 목록

    Returns:
        {
            "characters": [{name, description, visual_traits, t2i_prompt}],
            "locations": [{name, description, visual_traits, t2i_prompt}],
            "props": [{name, description, visual_traits, t2i_prompt}],
            "style": {era, region, genre, visual_tone},
        }
    r   u6   

[이전 에피소드에서 추출된 기존 요소]
F   rx   ua   
기존 요소가 이번 에피소드에도 등장하면 이름을 동일하게 유지하세요.
step1step1_resultz Step 1: restored from checkpointzStep 1: style + entity namesturn0_style)prior_blockrO   z

turn1entity_styleentity_step1)rt   system_promptuser_promptresponse_schemaproject_configschema_name)r   r.   r0   r1   r2   r3   r4   r5   r6   r7   )	r.   r0   r1   r2   r3   r4   r5   r6   r7   z3Step 1: %d chars, %d locs, %d props, style=%s/%s/%sr8   r9   r:   step2areview_resultz!Step 2a: restored from checkpointzStep 2a: GPT reviewr   )_load_lvm_promptc                 2    dj                  d | D              S )N, c              3   8   K   | ]  }|d     d|d    d  yw)r   (r'   u   회)Nr!   ).0es     r   	<genexpr>z1extract_entities.<locals>._fmt.<locals>.<genexpr>-  s(     REq&	{!Am,<+=TBEs   )ra   )r-   s    r   _fmtzextract_entities.<locals>._fmt,  s    99RERRRr   zturn1_review_schema.jsonentity_list_review)screenplay_summaryr8   r9   r:   entity_reviewuT   시나리오 분석 전문가. 추출된 요소 목록의 정확성을 평가한다.z'Step 2a GPT review failed, skipping: %sentitiesN)r   r   
importance2   
   r'   rw   r   z(Step 2a: filtering %d low-importance: %s	characterrF   propstep2bgpt_detailsz/Step 2b: restored from checkpoint (%d entities)z,Step 2b: GPT detail extraction (%d entities)
c              3   4   K   | ]  \  }}d | d| d  ywz- z ()Nr!   )r   r   etypes      r   r   z#extract_entities.<locals>.<genexpr>c  s$     $[l{tUr$r%%:l   z turn1_7_detail_batch_schema.jsonturn1_7_detail_batch)entity_listrO   ub   시나리오 분석 전문가. 요소별 시각적 상세 정보를 최대한 많이 추출한다.entity_detail_batchr+   rI   )r+   rI   z$Step 2b: GPT returned %d/%d entitieszStep 2b: %d missing, retryingc              3   4   K   | ]  \  }}d | d| d  ywr   r!   )r   nts      r   r   z#extract_entities.<locals>.<genexpr>  s#     (LGDAq2aS1#QGr   entity_detail_retryzStep 2b retry failed: %szStep 2b GPT detail failed: %s)r   r   r   zNo entities for T2I generationidxenamer   r   c                    j                  |i       }d}|r;|j                  dd      }dj                  |j                  dg             }d| d| }t        d||      |z   }t        d	      D ]}  }	 t	        d
|t
        d
dd
|gi      }	| |||	j                  d|      |	j                  dd      |	j                  dg       |	j                  dd      |	j                  d      xs i dfc S  | |||dg di dfS # t        $ rE}
t        j                  d||dz   |
       |dk  rt        j                  d|dz   z         Y d }
~
d }
~
ww xY w)Nr   r+   r   rI   u.   

[시나리오 기반 상세 정보]
설명: u   
시각적 특징: turn_entity_detail)entity_namerH      
entity_t2itags)rt   r   r   r   r   r   opik_metadatar   rJ   rK   )r   r+   rI   rJ   rK   z#Entity T2I %s attempt %d failed: %sr   rw   )ri   ra   r   ranger
   ENTITY_DETAIL_SCHEMArj   rg   warningtimesleep)r   r   r   
gpt_detailextradesctraitsturn_msgattemptdetailexcr   r   r   s              r   _gen_t2iz"extract_entities.<locals>._gen_t2i  s   $3JE!~~mR8:>>/2#FGKD6Qeflemn#$!! 	H !86,)&3$,(<'9$0'-e/D&EF   &

65 9'-zz-'D)/OR)H&,jjr&B)/O)D)J0   $0 b2UW!#(   ! 6NN#H%QX[\Q\^ab{

1!#456s   3A8C;;	E	;EE	
step3_donerH   uD   Step 3: T2I generation — %d entities (%d already done), %d workers)max_workersstep3)r   r   r   r   c                     | d   S )Nr   r!   )rs    r   <lambda>z"extract_entities.<locals>.<lambda>  s    qtr   )rn   z0Step 3: all %d entities restored from checkpointz7Entity extraction complete: %d chars, %d locs, %d props)r8   r9   r:   style)$rM   r"   rc   dumpsrW   ri   rg   rh   r   r
   STEP1_SCHEMAr   len'app.modules.pipeline.ref_image_pipeliner   r   rj   r   intappendra   r   r	   r   r   	enumerater-   minr   r   r   submitr   resultsortr   )6rO   rN   r   r   cpr    r   r   step1_promptr   r   r   r   review_schemareview_promptr   entities_reviewr   low_importanceentity_queueentity_list_textdetail_schemadetail_promptbatch_resultentr   r   missingmissing_textretry_promptretry_result	retry_excr8   r9   r:   totalr   r   iremaining_queuer   datar   r   resultsexecutorfuturesfuturer   __idx_enamer   r   s6     `                                                 @@r   extract_entitiesr      s'	   > 
^X	.B^FKGjjeAFGst 	 
R..'9vvn-6723#M{U]^g;Ya!bbb& $(-&
 	l3 (++,=rBr*""8R0!!'2.&**+;R@&**+;R@%))/2>"&&|R8"&&|R8
E KKEL$$\267L$$["56L$$Wb12eeHouW~	? 
R..(:/78)*L	S %%?@( 'L,,\2>?<++K<=|''45
	-+$t) -1+M 	|=Q $''
B7O**aquu\2&'",QUU=!5L1MPQ1Q 	
&	?   >N@SUcd*,LlB/V9N*6K 89 0 k2.V9N*6J 78 / gr*V9N*6F 34 +
 
R..(:ff]B/Es;GWXBCDUV1399$[l$[[3	A()KLM(&,!M +$ C) -11L $''
B7#&77="#=%(WW_b%A,CK( 8
 KK>K@PRUVbRcd +7O,$!Q!;:N1v,GO>GM#yy(LG(LL+* ,% 
J#2, 'K$0(5'9$9$L  ,//
B?+.77="+E-0WW_b-I4CK0  @ 	|=^ij (*J&(I"$EEz45$)	# )	c )	# )	%S#tTWY\T\~@]:^ )	X 13|R0H
6?6Me6MFQQRZdQdAq!96Me%++-KE4HH]K8E#!!$'*$  &T" . b#o"67KKK^s:=BDG#<'6OAue

1JKGHOOHaFG (7
 +73F#]]_FNN6*,2)AueT*/D'(,Ju%GGG,m(3
  L 4 =" LL^L,-4)feTK'%%d+j($$T*LL& .5 KKJCPZO\
KKIJYU=IIK !	 I  	-NNDcJ',M	-h P. ! JNN#=yIIJ  	ANN:C@@	Az f" =<s   #b  Ac3Bd	 cc A
d	 +Ac >d	 0d6 d6;Bd= 	c)c		cd	 	d%d;d	 dd	 		d3d..d3=e)NNN)"__doc__r\   rc   loggingrZ   r   concurrent.futuresr   r   typingr   r   r   r   r	   app.modules.llm.llm_clientr
   r   app.modules.prompt_loaderr   r   	getLoggerr   rg   r   r   r   r   r"   _ENTITY_BRIEF_ITEMr   r   rM   r   r!   r   r   <module>r	     s  	    	  ? 3 3 F >			8	$
0s 0 0&s &tCH~ &
"c " "	* '!   %-=jkh' E

 !8$(#!8,!8, (+x(x(&1CD%0BC!,>?'*
 "9@ "))h'
 x(  ($, /,4 5=5C]4S1*
 5<6<h5G?*
 -3Xv4F>*3& 1d<A!2"+( *9(949/> ($, 4vy6I+ *>(>49
$?,Z $%67$)a1
+Gb g!iU t#5 #5T %))-59	ttSMt !t T$sCx.12	t
 
#s(^tr   