
    jl:                       d Z ddlm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
 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 ddlmZmZmZ ddlm Z   ejB                  e"      Z#ddZ$ddZ% G d d      Z&y)u  ReferencePhase3Service — Phase 3 (character + outlook composite) 단독 서비스.

F24.4.4 (2026-04-24): `ReferencePipelineOrchestrator.run()`의 Phase 3 블록
(face + outfit → 전신 composite 이미지 병렬 생성)을 literal lift.

ctx.composite_generated / ctx.composite_skipped를 in-place 업데이트한다.
외부 예외는 원본과 동일하게 내부에서 log + swallow — run()은 None 반환.
    )annotationsN)ThreadPoolExecutoras_completed)datetimetimezone)Path)DictList)Session)to_relative_image_path)annotate_generated_asset)bind_current_budget)ActivityLogger)CharacterOutlookEntityCanon
ImageAsset)ReferencePipelineContextc                 d    t        j                  t        j                        j	                         S N)r   nowr   utc	isoformat     [/Users/manta/Documents/Projects/TheRoad-I1/backend/app/services/reference_phase3_service.py_nowr      s    <<%//11r   c                 <    t        t        j                               S r   )struuiduuid4r   r   r   _new_idr!   #   s    tzz|r   c                  4    e Zd ZdZ	 	 	 	 	 	 	 	 	 	 ddZddZy)ReferencePhase3ServiceuH   Phase 3: face + outlook → 전신 composite reference 이미지 생성.c                <    || _         || _        || _        || _        y r   )_db_project_id	_actor_id_logger)selfdb
project_idactor_idactivity_loggers        r   __init__zReferencePhase3Service.__init__*   s!     %!&r   c                   %&'() ddl m( |j                  &|j                  }|j                  }|j
                  '|j                  )|j                  }	 ddl m% ddl	m
}  | j                   j                  &      }i }|D ]7  }|j                  |j                  g       j                  |j                          9  j                  j#                  t$              j'                  t$        j(                   j                  k(  t$        j*                  dk(  t$        j,                  dk(        j/                         D 	ch c]  }	|	j0                   }
}	g }|j3                         D ]+  \  }}||v r|xj4                  t7        |      z  c_        *|D ]  }||
v r|xj4                  dz  c_         j                  j#                  t8              j'                  t8        j:                  |k(  t8        j<                  dk(  t8        j>                  dk(  t8        j@                  jC                  d	       t8        j@                  jC                  d
             jE                         }|r#tG        |jH                        jK                         stL        jO                  d|       
tG        |jH                        jQ                         }d| d| d|j0                   } j                  j#                  t8              j'                  t8        j(                   j                  k(  t8        j@                  jC                  d| d      t8        j<                  dk(        jE                         }|r:tG        |jH                        jK                         r|xj4                  dz  c_        	 j                  j#                  t8              j'                  t8        j:                  |k(  t8        j<                  dk(  t8        j>                  dk(        jE                         }||v }tS        |xr# tG        |jH                        jK                               }|s|stL        jO                  d|       |r#tG        |jH                        jQ                         nd} j                  j#                  t$              j'                  t$        j0                  |k(        jE                         } j                  j#                  t$              j'                  t$        j0                  |k(        jE                         }|r|s|j                  ||jT                  ||jT                  ||r|jT                  |fgng ||j0                  |r|s|j0                  nd ||v |jV                  xs dd        . |rtL        jY                  dt7        |             %&'() fd}t[        t]        |t7        |                  5 }t_        |      }|D ci c]  }|ja                  ||      | }}tc        |      D ]P  }||   }	 |je                         \  } }!t9        tg                j                  d|d   &ti        |!d         d|d    d|d    d|d    |!d   dtk        jl                  |!jo                  d i       d!"      |!jo                  d i       jo                  d#      dtq               $      }" j                  js                  |"       tu        |"d%d%|d&   |jo                  d'      fD #cg c]  }#|#r|# c}#(        j                  jw                          |xjx                  dz  c_<        tL        jY                  d)|d   |d          S 	 d d d        y y c c}	w c c}w c c}#w # tz        $ r)}$tL        jO                  d*|d   |d   |$       Y d }$~$d }$~$ww xY w# 1 sw Y   y xY w# tz        $ r }$tL        jO                  d+|$       Y d }$~$y d }$~$ww xY w),Nr   )generate_and_validate_reference)_load_ref_image_prompt)episode_outlook_rowsO00outlook   	referencez[composite:%z[outlook_id:%z2Composite SKIPPED: face ref missing for char_id=%sz
composite::%z7Composite SKIPPED: outfit ref missing for outlook_id=%sr    )char_id	char_name
outlook_idoutlook_name
face_bytesoutfit_bytes_listcomposite_keyface_asset_idoutfit_asset_idbody_identityoutlook_descriptionz3Generating %d composite images (face + outfit refs)c                Z   | j                  dd      }|rdnd}|| d   fg}|s#| d   D ]  \  }}|j                  d| |f         |rdnd	      }|sd
} 
| d    d| d    d| d    d| d    |rdnd	||| j                  d      xs ddj                  	d| d   d
      }| |fS )NrC   Fu3   Reference image 1 — the character's COMPLETE BODYzReference face imager>   r?   zOutfit piece: composite_derive	compositezyFull body shot, standing pose, plain neutral background. Dress the character in the outfit shown in the reference images.r;   z (r=   )z	 wearing rD   r9   ref_image_gen_compositer:   )r+   
episode_idoperation_type	entity_id)
gemini_cliententity_nameentity_descriptionentity_type
t2i_prompt
output_dirextra_referencesrD   style_context
trace_meta)getappendr&   )task_derive_base_labellabeled_refsoutfit_labeloutfit_bytescomposite_promptpipe_result_load_composite_promptrJ   rM   r0   reference_dirr)   s           r   _gen_compositez2ReferencePhase3Service.run.<locals>._gen_composite   s-    #hh>G&- $Y3I  %0$|2D$E#FL #:>?R:S6L,(//#1,!@, OQ ;T (>.5*;(H$+ ,g("A&3'+K'8&9D<P;QQR$S.2;.?-@	$~J^I_+` <C%7*5#3#0)5 -1HH5J,K,Qr&( +/*:*:*4.G)-i	$##K0  ,,r   )max_workersr:   	file_path[r@   z] r;   +r=   generation_model	generated
validationF)ensure_asciiscore)idr+   
asset_typerL   rJ   rd   prompt_usedrg   statusreview_notesvalidation_score
is_primary
created_atreference_compositerA   rB   )pipeline_rolestageinput_image_idszComposite generated: %s + %sz Composite failed for %s + %s: %szComposite generation error: %s)>'app.modules.pipeline.ref_image_pipeliner0   rJ   low_freq_skip_idsbody_identity_char_idsrM   ra   max_concurrentr1   app.core.entity_identityr2   r%   r&   
setdefaultcharacter_idrW   r<   queryr   filterr+   short_idrP   allrl   itemscomposite_skippedlenr   rL   rm   rr   rn   likefirstr   rd   existsloggerwarning
read_bytesboolnamedescriptioninfor   minr   submitr   resultr!   r   jsondumpsrV   r   addr   commitcomposite_generated	Exception)*r)   ctx_low_freq_skip_ids_body_identity_idsr{   r2   char_outlooks_allchar_outlook_groupscoe_o00_idscomposite_tasksr:   outlook_idsr<   
face_assetr>   r@   existingoutfit_asset
_is_derive_has_outfitr]   char_entoutlook_entrb   executor_submit_gen_compositetfuturesfuturerX   _r_   _composite_assetxexcr`   rJ   rM   r0   ra   s*   `                                    @@@@@r   runzReferencePhase3Service.run6   s/   [ ^^
 22 77))))++Z	Bp F 4$**J!8 9;'#..rCJJ2==Y ( #hhnn[9@@**d.>.>>((E1++y8 #%	    !O(;(A(A(C$00))S-==)"-J!X---2-  z2&00G;&11[@&11Q6'3388HH'3388II   &T*2F2F-G-N-N-P'[]de !%j&:&:!;!F!F!HJ %WIQzl!JMM?K " z2&11T5E5EE&2277!M?!8LM&11[@
    D););$<$C$C$E--2-  z2&00J>&11[@&11Q6
  ! "),>!>J"&$Nl.D.D)E)L)L)N#PK&z'`blm  ' \334??A,/ !  $xx~~k:AA+..T[B[\bbdH"&((.."="D"D[^^WaEa"b"h"h"jK#; #**#*%-]]&0(3(8(8&0BNk..=>TV)6)3 1<JLOO!%)04F)F 1<0G0G0M2', I #.	 )D| QSVWfSgh6- 6-p (COH\4]^bj -@,O)UdeUdPQx/DaH!KUdGe".w"7&v!Y-3]]_NA{/9#*9+/+;+;+6*.y/+5*@[AY*Z./_0E/FbkIZH[[\]abp]q\r,s1<=O1P'2-1ZZVX8Yhm-n1<r1R1V1VW^1_+,+/60, !HHLL)9:4 0.C&;04_0E049J0K0M1*0M!'( %& 0M1*	 !HHOO-33q83"KK(F[HY[_`n[opA #8 _^w OL f41*  ) Y"NN+M+/+<d>>RTWY YYM _^T  	BNN;SAA	Bs   C%_ ;]2R_ ^6,]7^6C(^]<A^$^6(_ 2_ 7^6<^	^3
^.(^6.^33^66^?;_ ?_ 	_+_&&_+N)
r*   
OrmSessionr+   r   r,   r   r-   r   returnNone)r   r   r   r   )__name__
__module____qualname____doc__r.   r   r   r   r   r#   r#   '   sA    R
'
' 
' 	
'
 (
' 

'eBr   r#   )r   r   )'r   
__future__r   r   loggingr   concurrent.futuresr   r   r   r   pathlibr   typingr	   r
   sqlalchemy.ormr   r   app.core.file_pathsr   #app.services.image_capture.annotater   app.core.image_call_budgetr   app.logging.activity_loggerr   app.models.projectr   r   r   'app.services.reference_pipeline_contextr   	getLoggerr   r   r   r!   r#   r   r   r   <module>r      sf    #    ? '   0 6 H : 6 H H L			8	$2tB tBr   