
    )j\                       d Z ddlm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
 ddlmZmZmZmZ ddlmZ ddlmZ dd	lmZmZmZ dd
lmZ ddlmZ ddlmZ ddlm Z m!Z!m"Z"m#Z#m$Z$m%Z%m&Z&m'Z'm(Z( ddl)m*Z*m+Z+ ddl,m-Z- ddl.m/Z0 ddl1m2Z2 ddl3m4Z4 ddl5m6Z6 ddl7m8Z8 ddl9m:Z: ddl;m<Z< ddl=m>Z>  ej~                  e@      ZAddZBddZC G d d      ZDy)u  ReferencePipelineOrchestrator — Phase 1/2/3 참조 이미지 생성 orchestration.

Phase 3b.2 ~ F24.3: reference_image_service.py의 ``generate_reference_images_only``
(666 LOC) 본체를 literal lift. facade ``ReferenceImageService``는 본 서비스의
``run(...)``에 얇게 위임한다.

이후 F24.4.2~4에서 Phase1/2/3 서비스로 분해될 예정 (현재는 단일 run 메서드).
    )annotationsN)datetimetimezone)Path)AnyDictListOptional)Session)settings)$collect_reference_required_canon_idscompute_variant_pole_idsshould_skip_low_freq)AppError)t)ActivityLogger)	CharacterOutlookEpisodeEntityCanonEntityEpisodeLink
ImageAssetRelationFactRelationParticipant
SceneStill
WorldGuide)build_visual_dependency_graphtopological_sort_entities)GeminiImageClient)	key_count)OpenAIClient)ProgressTracker)WorldGuideGenerator)ReferencePhase1Service)ReferencePhase2Service)ReferencePhase3Service)ReferencePipelineContextc                 d    t        j                  t        j                        j	                         S N)r   nowr   utc	isoformat     b/Users/manta/Documents/Projects/TheRoad-I1/backend/app/services/reference_pipeline_orchestrator.py_nowr/   :   s    <<%//11r-   c                 <    t        t        j                               S r(   )struuiduuid4r,   r-   r.   _new_idr4   >   s    tzz|r-   c                  Z    e Zd ZdZ	 	 	 	 	 	 	 	 	 	 ddZd	dZ	 	 d
dd	 	 	 	 	 	 	 	 	 ddZy)ReferencePipelineOrchestratoru9   Phase 1/2/3 참조 이미지 생성 orchestrator (F24.4).c                    || _         || _        || _        || _        t	        ||||      | _        t        ||||      | _        t        ||||      | _	        y )N)db
project_idactor_idactivity_logger)
_db_project_id	_actor_id_loggerr#   _phase1_svcr$   _phase2_svcr%   _phase3_svc)selfr8   r9   r:   r;   s        r.   __init__z&ReferencePipelineOrchestrator.__init__E   sp     %!&1!+	
 2!+	
 2!+	
r-   c                
   | j                   j                  t              j                  t        j                  |k(  t        j
                  | j                  k(        j                         }|st        dt        d      d      |S )Nzepisode.not_foundi  codemessagestatus_code)
r<   queryr   filteridr9   r=   firstr   r   )rC   
episode_ideps      r.   _get_episodez*ReferencePipelineOrchestrator._get_episodeg   sl    HHNN7#VGJJ*,g.@.@DDTDT.TUUW 	
 (-. 
 	r-   NF)skip_compositec                  |dvrt        d|d      | j                  |      }|j                  st        dt	        d      d      |j
                  dk7  rt        dd	|j
                   d
d      | j                  j                  t              j                  t        j                  | j                  k(        j                         }|dk(  rt        ddd      t        j                  s$t               dk(  rt        dt	        d      d      |j                   xs d}t#        t        j$                        | j                  z  }|dz  |z  dz  }	ddlm}
  |
| j                  | j                  |      }|rY| j                  j                  t              j                  t        j*                  j-                  |            j/                         ng }|D cg c]b  }|j*                  |j0                  |j2                  |j4                  xs d|j6                  xs d|j8                  xs d|j:                  xs ddd }}ddlm}m }m!}m"}m#}m$}m%}  || j                  | j                        } || j                  | j                  ||      } ||      }|D ci c]+  }|d   |jM                  |jM                  d      xs d|      - }}tO               }i }tO               }|dk(  r|D ]z  }| j                  j                  tP              j                  tP        j                  | j                  k(  tP        jR                  |d   k(  tP        jT                  dk(  tP        jV                  dk(        jY                  tP        jZ                  j]                               j_                         }|st#        |j`                        }|jc                         s&td        jg                  d|d   |j`                          | ||      ||d            s/|ji                  |d          td        jk                  d|d          Q|ji                  |d          |jm                         ||d   <   } to               } | j                  j                  tp              j                  tp        j                  | j                  k(  tp        jr                  |k(  tp        jt                  dk(  tp        jv                  dk\  tp        j
                  d k7        j/                         }!|!D "cg c]  }"d!|"jx                  xs di }#}"|j                  xs d}$t{        j|                  |$ d"t        |       d"t        |#       j                               j                         }%| j                  j                  t              j                  t        j                  | j                  k(  t        jr                  |k(        jY                  t        jZ                  j]                               j_                         }&|&r/|&j                  |%k(  r t        j                  |&j                        }'nt        | #      }(|(j                  |j                  ||j                  xs d$||#%      }'t        t               | j                  |t        j                  |'d&'      |%t               (      })| j                  ji                  |)       | j                  j                          t        t        j                  )      }*dd*lPmQ}+ |d+z  dz  |z  }, |+|,d      }-|-j                         D ]  }.|-j                  jM                  d,i       jM                  |.i       }/|/jM                  d-d      }0|0rt#        |0      jc                         r{|.|v ra ||/jM                  |      |jM                  |.|            s<|j                  |.       |ji                  |.       td        jk                  d.|.d/d0        |ji                  |.       td        jg                  d1|.d/d0         | j                  j                  t              j                  t        j                  | j                  k(        j/                         }1|1D 2cg c]  }2|2j*                   }3}2|3rY| j                  j                  t              j                  t        j                  j-                  |3            j/                         ng }4t        ||1D 2cg c]  }2|2j*                  |2j                  d2 c}2|4D 5cg c]  }5|5j                  |5j                  d3 c}5      }6t        ||6      }7i }8| j                  j                  t              j                  t        j                  | j                  k(  t        jr                  |k(        j/                         D ]  }9|9j                  xs d|8|9j                  <   ! tO               }:|6j                         D ]  };|:j                  |;        t        | j                  | j                  |      }<tO        |<      }=t        ||1D 2cg c]  }2|2j*                  |2j                  d2 c}2|4D 5cg c]  }5|5j                  |5j                  d3 c}5      }>tO               }?g }@dd4lbmc}A tO         |A             }B|D ]  }|d   }C|jM                  d5d      }D|DBvr|8jM                  Cd      }E|C|:v }F|C|>v }G|C|=v }Ht        ||E|F|G|H      }I|Ir|?ji                  C       d6}Jn"Hr	d7|<C    }JnEdkD  rd8E }JnFrd9}JnGrd:}Jnd;}J@j                  C|jM                  d      DIJEd<       td        jk                  d=|jM                  d      |D|EFGH|I        dd>lfmg}K  |K| j                  |@       |D ci c]  }|d   |
 }L}d}Md}Nt        |?      }Ot        |      |Oz   }Pt        j                  }Qt        |      |Pz
  }Rt        | j                  |d?| j                        }S| j                  j                  t              j                  t        j                  | j                  k(  t        j4                  d@k(  t        j2                  dAk(        j/                         D ch c]  }|j*                   }T}tO               }U|TrSddBlmj}V  |V| j                  | j                  |      D ],  }W|Wj                  Tv sUji                  Wj                         . ddClmmn}X  |X| j                  | j                  |      }Y|Yrtd        jk                  dDt        Y             t        dai dE|d$|dF|dG|dH|dI|dJLdK|6dL|7dM|	dN|*dOQdPSdQ|-dR|dS|dT|?dUUdVYdW|dXddYddZOd[P}Z| j                  j                  |Z      }[|[[S Zj                  }M|Zj                  }N|r$Sj                          MPONt        |      dddd\S | j                  j                  Z       |Zj                  }\|Zj                  }]| j                  j                  |Z       |Zj                  }^|Zj                  }_Sj                          | j                  j                  | j                  d]d$|| j                  MPO|\|]|^|_d^|_       |M|P|O|\|]d`S c c}w c c}w c c}"w c c}2w c c}2w c c}5w c c}2w c c}5w c c}w c c}w )bu/  엔티티 참조 이미지 생성 orchestrator — Phase 1 (base ref) + Phase 2/3 (composite).

        Phase 3b.4: 신규 호출자는 `generate_base_references` / `generate_composites`
        public API를 사용할 것. 본 함수는 기존 호출자(api/v1/images.py 및
        backend/scripts/experiment_*.py) 호환을 위해 `skip_composite` 플래그 시그니처로
        유지된다.

        mode: "resume" 만 valid. "full" 모드는 feedback_never_delete_images
              규칙 위반 (ImageAsset 일괄 DELETE + cp.clear) 으로 영구 차단.
              강제 재생성은 RefImageGenStep mode='force' 사용.

        skip_composite (Phase 3.1 step 경계 재정의):
            True → Phase 1(base reference)까지만 수행, Phase 2(character+outlook composite) 생략.
            RefImageGenStep이 이 플래그로 호출하여 자기 책임(ref)만 수행.
            CompositeImageGenStep은 기본값(False)로 호출하여 resume 모드에서 이미 완료된
            reference는 skip되고 composite만 생성됨.
        )resumez(ReferencePipelineOrchestrator.run: mode=z unsupported. Only 'resume' is allowed (feedback_never_delete_images). Use RefImageGenStep mode='force' for non-destructive regeneration.zanalysis.no_texti  rF   analyzedzimage.analysis_not_completeu0   분석이 완료되지 않았습니다 (현재: u$   ). 분석을 먼저 완료하세요.r   zimage.no_entitiesuC   추출된 요소가 없습니다. 분석을 먼저 실행하세요.zimage.gemini_key_missingkoimages	reference)active_episode_canon_ids z{})rL   nameentity_typeshort_iddescriptionstable_traits
t2i_prompt)EMPTY_INPUT_DIGESTGROUNDING_INPUT_DIGEST_KEYdigests_by_final_idis_freshproject_grounding_modestored_digest_of_assetverified_policy_references)r9   rN   grounding_moderL   r\   rS      u2   Ref image file missing, will regenerate: %s → %srZ   z>Ref image stale (grounding input changed), will regenerate: %sTstalestill_frame_prompt:)
llm_clientepisode)fulltextlanguagesource_fileentitiesstillsF)ensure_ascii)rL   r9   rN   
guide_jsonsource_hash
created_at)model)ImageCheckpointManagercheckpoints	completed	file_pathz2Ref checkpoint stale (grounding input changed): %sN   z0Checkpoint ref file missing, will regenerate: %s)rL   relation_family)relation_idcanon_id)canonical_ref_owner_typesr[   z-no required_ref + low t2i count + not variantzprotected: zprotected: t2i_count=zprotected: base_for_variantzprotected: variant_self	protected)r   rZ   r[   skippedreason	t2i_countzgLow-freq decision: %s (%s, t2i_count=%d, base_for_variant=%s, variant_self=%s, required=%s, skipped=%s))save_low_freq_skip_reportreference_image_generationO00outlook)episode_outlook_rows)body_identity_entity_idsu!   몸이 곧 신원인 인물 %d명rN   moderQ   iprq   entity_by_iddepsbatchesreference_dirgemini_clientmax_concurrentprogressref_cpref_image_mapalready_donelow_freq_skip_idso00_char_idsbody_identity_char_idsgrounding_referencesgenerated_countfailed_countlow_freq_skippedskipped_count)	generatedr   r   failedtotaloutlook_generatedoutlook_skippedskipped_composite_phasez!episode.generate_reference_images)r   r   r   r   r   composite_generatedcomposite_skipped)r:   actionresource_typeresource_idr9   detail
ip_address)reference_countr   r   r   r   r,   )~
ValueErrorrP   rn   r   r   statusr<   rJ   r   rK   r9   r=   countr   gemini_api_keygemini_key_countro   r   projects_dirapp.core.entity_identityrX   rL   in_allrZ   r[   r\   r]   r^   r_   3app.modules.pipeline.grounding_canonical_ref_inputsr`   ra   rb   rc   rd   re   rf   getsetr   	entity_id
asset_type
is_primaryorder_byrv   descrM   r{   existsloggerwarningaddinfo
read_bytesr    r   rN   is_selectedstill_indexrj   hashlibmd5lenencode	hexdigestr   ru   jsonloadsrt   r"   generatesource_filenamer4   dumpsr/   commitr   gemini_image_modelapp.modules.image_checkpointrx   get_completed_ids_datadiscardr   r   r~   r   r}   r   r   r   t2i_appearance_countvaluesupdater   r   .app.modules.pipeline.grounding_entity_contractr   r   appendapp.core.low_freq_skipr   max_concurrent_image_genr!   r   
outlook_idcharacter_idapp.core.body_identityr   r&   r@   runr   r   completerA   r   r   rB   r   r   r?   logr>   )`rC   rN   r   r   rQ   rm   entity_countro   project_dirr   rX   	canon_idsentities_ormerq   r`   ra   rb   rc   rd   re   rf   _grounding_moder   _digest_by_fidwant_digest	stale_idsr   r   entityexistingfpopenai_client
stills_ormsrr   _ftwg_hashexisting_wgworld_guidewg_gen	wg_recordr   rx   cp_dirr   cp_idcp_datacp_pathrelations_ormrrel_idsparticipants_ormpr   r   _t2i_count_map_lnk_reverse_dep_ids_dep_set_required_reason_by_uuid_required_canon_uuids_variant_pole_uuids_low_freq_skip_ids_skip_decisionsr   _canonical_owner_typeseidetyper   is_base_for_variantis_variant_selfrequiredr   _reasonr   r   r   r   r   r   r   total_to_genr   _o00_outlook_ids_o00_char_idsr   cor   _body_identity_char_idsctxearly_returnr   r   r   r   s`                                                                                                   r.   r   z!ReferencePipelineOrchestrator.runy   s   8 {":4( CU U  ##J/ 2A>P<Q_bcc>>Z'2J7>>JZZ~ 
 xx~~k299""d&6&66

%' 	 1(] 
 &&+;+=+B :AF`Daorss##+t8001D4D4DD#h.;kI
 	F,HHd&&
4	  xx~~k299NNy)

#%" 	 "

 "	 44)rMM/R!//BYUY<<-2/ " 	 
	
 	
 	
 14;K;KL9HH!1!1j* , --AB '
 dG^''j(9(?RASTT 	 '
 	*,E8"HHNN:.V"--1A1AA",,t<"--<"--2	 Xj3388:;UW  h001B99;'[]cdj]kmumm  A%&<X&F&1&,&?A
 "fTl3$d$*6N4 %((668mmofTl35 #8 %XX^^J/66!!T%5%55!!Z/""d*""a'(
 #% 	 OYYj')=)=)CDjY$"++Qs8}oQs6{mDKKMNXXZhhnnZ077!!T%5%55!!Z/
 (:((--/
0 	
 ;22g=**[%;%;<K(MBF // ))H#33@y!& * K
 #9)9)9j::kF#I
 HHLL#HHOO)0K0KLG},x7*D'<--/Ell&&{B7;;E2FGkk+r2G4=//1 I%,F G +7I JL ((/MM%(KK TV[\^]^V_`  'QSXY[Z[S\]! 0$ |4;;L<S<SW[WgWg<ghlln!./A144/   88>>*=>EE++//8

#% 	 -IVWAADDQ->->?WM]^M]Q]]

CM]^

 ,Hd; *,HHNN#45<<((D,<,<<((J6
 #%D -1,E,E,JN4==)	 !$H##H- & $HHHd&&
$
  &))A%B 7IVWAADDQ->->?WM]^M]Q]]

CM]^
 #&% "	
 "%%>%@!AAD'CEE-,E22"&&sA.E"%)9"9!%88O33H*5-G "&&s+I'(@(E'FG1%9$7 3%""f$"!"$  KK<fue-@7	C P 	E!j/	
 -55Hq$
H512L),<<!::8}}4"488Z9UW[WgWgh ((..5<<&&$*:*::$$-''94 ce	
QADD  	 
 E*488T5E5E+57==$44!%%boo67 	D":HHd&&
#4"KK;SAX=YZ ' 
!

 
 *	

 
 
 &
 
 
 (
 (
 *
 
 
 (
  &!
" 1#
$ '%
& $;'
( "6)
* +
, -
. ./
0 (1
4 ''++C0# --'' ,($4&X%&#$+/	 	 	S!11-- 	S!!5511^^,O#'',($4%6#2':%6  	 	
   /$ 0!2.
 	
Y

6'
Z Zf 0 X^F X^l 6
s=   A'{0{{{ { 
3 {% {*
? {/ {4){9)
r8   
OrmSessionr9   r1   r:   r1   r;   r   returnNone)rN   r1   r  r   )NrS   )
rN   r1   r   zOptional[str]r   r1   rQ   boolr  zDict[str, Any])__name__
__module____qualname____doc__rD   rP   r   r,   r-   r.   r6   r6   B   s    C

 
 	

 (
 

D* !	Y
  %Y
Y
 Y
 	Y
 Y
 
Y
r-   r6   )r  r1   )Er  
__future__r   r   r   loggingr2   r   r   pathlibr   typingr   r   r	   r
   sqlalchemy.ormr   r  app.core.configr   app.core.entity_protectionr   r   r   app.core.errorsr   app.i18n.loaderr   app.logging.activity_loggerr   app.models.projectr   r   r   r   r   r   r   r   r   app.modules.entity_dependencyr   r   #app.modules.llm.gemini_image_clientr   app.modules.llm.gemini_key_poolr   r   app.modules.llm.openai_clientr    app.modules.progress_trackerr!   !app.modules.world_guide_generatorr"   %app.services.reference_phase1_servicer#   %app.services.reference_phase2_servicer$   %app.services.reference_phase3_servicer%   'app.services.reference_pipeline_contextr&   	getLoggerr  r   r/   r4   r6   r,   r-   r.   <module>r6     s    #     '  , , 0 $ 
 %  6
 
 
 B I 6 8 A H H H L			8	$2P
 P
r-   