
    mjy                       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	 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 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  ddl!m"Z#m$Z$ ddl%m&Z'm(Z) ddl*m+Z+m,Z,m-Z-  ej\                  e/      Z0ddZ1ddZ2	 	 	 	 	 	 	 	 	 	 ddZ3	 	 	 	 	 	 	 	 	 	 ddZ4	 	 	 	 	 	 ddZ5 G d d      Z6y)u  씬 이미지 변형(variant) 선택 및 편집 서비스 — W5 F22 Phase B.

SceneImageService에서 variation 관련 로직을 이관. 공개 API는 facade가 유지하고
본 서비스는 구현을 담당.

공개 API (SceneImageService facade가 delegation):
- Phase B.1: `select_variant` / `select_original`
- Phase B.2: `regenerate_variation` (fal.ai 앵글 i2i)

Module-level helper (facade + variation_svc 양쪽에서 공유):
- `set_variant_primary(db, project_id, image_id, still_id)`
    )annotationsN)datetimetimezone)Path)AnyDictListOptional)Session)settings)AppError)to_relative_image_path)annotate_generated_asset)t)ActivityLogger)
ImageAsset
SceneStill)	key_countget_next_key)apply_fal_angleselect_final_best)build_lineage_fieldsimage_to_dictload_project_llm_configc                 d    t        j                  t        j                        j	                         S N)r   nowr   utc	isoformat     Z/Users/manta/Documents/Projects/TheRoad-I1/backend/app/services/scene_variation_service.py_nowr#   -   s    <<%//11r!   c                 <    t        t        j                               S r   )struuiduuid4r    r!   r"   _new_idr(   1   s    tzz|r!   c                   | j                  t              j                  t        j                  |k(  t        j                  |k(  t        j
                  |k7        j                         }|D ]	  }d|_         | j                  t              j                  t        j
                  |k(        j                         }|rd|_        yy)u   해당 image를 still의 primary로 설정 (siblings는 is_primary=0).

    scene_image_service / scene_variation_service 양쪽에서 공유되는 helper.
    commit은 호출자 책임.
    r      N)	queryr   filter
project_idstill_ididall
is_primaryfirst)dbr-   image_idr.   siblingssibtargets          r"   set_variant_primaryr8   5   s     		!!Z/8+MMX%


 
    XXj!((()BCIIKF r!   c                *   d }|t        |       k  r| |   n| d   } ||      dk  rjt        |       dkD  r\| d||rdndz
   }|rNt        ||d      } ||d          ||      dz   kD  r)| j                  |d         }| ||       ||d         ffS |dfS )	u  Validation score 기반 best_idx override.

    W5 F22 Phase B.22.7 (2026-04-23): scene_image_service.generate_images의
    validation_score override 블록(~16 LOC)을 이관.

    처리:
      1. 현재 선택된 variation의 validation_score (None → 50) 조회
      2. score < 40이고 variation 2+개면 override 후보 탐색
      3. fal.ai 결과 제외한 original N개 중 가장 높은 score 후보 선택
      4. 후보 score가 현재보다 20점 초과 높으면 override (strict >)

    Args:
      var_results_list: 전체 variation 결과 (fal.ai 결과 포함 가능)
      best_idx: Gemini Vision이 선택한 (또는 fallback된) 현재 best 인덱스
      num_saved_assets: DB에 저장된 asset 개수 (var_results_list 총 길이와 동일)
      fal_generated: fal.ai N+1 이미지가 추가되어 있는지

    Returns:
      (new_best_idx, override_scores):
        - override_scores=None: override 발생 안 함, best_idx 그대로
        - override_scores=(old, new): override 발생, caller 로그용
    c                0    | j                  d      }||S dS )Nvalidation_score2   )get)rss     r"   _vscorez*apply_validation_override.<locals>._vscorem   s     EE$%Mq)r)r!   r   (   r*   NT)keyreverse   )lensortedindex)	var_results_listbest_idxnum_saved_assetsfal_generatedr@   selected_resultorig_resultsalternativesoverride_idxs	            r"   apply_validation_overriderP   Q   s    8* c*++ 	"a  
 "$-=)>)B'(X*:=aVW*XY!,GTJL|A''/*BR*GG/55l1oF#O,LO,&   T>r!   c                &   d}d}	 g }t        |       D ][  \  }}|j                  d      rt        |d         nd}|s*|j                         s;|j	                  ||j                         f       ] t        |      dkD  rQ|D cg c]  \  }}|	 }	}}|D 
cg c]  \  }}
|
	 }}}
t        ||      }|	t        |t        |	      dz
           }d}||fS c c}}w c c}
}w # t        $ r'}t        j                  d|       d}d}Y d}~||fS d}~ww xY w)uF  Gemini Vision으로 variations 중 최종 대표 이미지 인덱스 선택.

    W5 F22 Phase B.22.5 (2026-04-22): scene_image_service.generate_images의
    Gemini Vision final best 선택 블록(~22 LOC)을 이관.

    처리:
      1. var_results_list 순회, 존재하는 file_path의 bytes만 valid_pairs로 수집
      2. len(valid_pairs) > 1이면 _select_final_best 호출 → raw_best 인덱스
      3. valid_indices[min(raw, len-1)]로 원래 인덱스 복원
      4. 예외 또는 valid_pairs <=1이면 (0, False) 반환

    Returns:
      (best_idx, was_selected):
        - was_selected=True: Gemini Vision이 실제로 선택을 수행함 (caller가 로그 출력)
        - was_selected=False: valid_pairs<=1 또는 예외로 선택 스킵 (best_idx=0 fallback)

    beat_title은 Gemini Vision prompt에 사용.
    r   F	file_pathNr*   Tz,Final best selection failed, using first: %s)	enumerater=   r   existsappend
read_bytesrE   _select_final_bestmin	Exceptionloggerwarning)rH   
beat_titlerI   was_selectedvalid_pairsvivrfp_valid_indicesbvalid_bytesraw_bestexcs                 r"   select_best_variation_idxrh      s!   , HL 01FB*,&&*=bo&4Bbiik""B#89 2
 {a-89[EBR[M9)45A1K5)+zBH$S3}3E3I%JKHL
 \!! :5  EsK\!!	s@   6C  C  6C  CC  C$,C  C   	D)DDc                      e Zd ZdZ	 	 	 	 	 	 	 	 ddZ	 d	 	 	 	 	 	 	 ddZddZ	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddZddZ	 d	 	 	 	 	 ddZ		 	 	 d	 	 	 	 	 	 	 	 	 dd	Z
	 d	 	 	 	 	 	 	 dd
Zy)SceneVariationServiceuA   씬 이미지 variant 선택 전담 서비스 (W5 F22 Phase B.1).c                N    || _         || _        || _        t        |      | _        y r   )_db_project_id	_actor_idr   _logger)selfr3   r-   actor_ids       r"   __init__zSceneVariationService.__init__   s&     %!%b)r!   Nc           	        |dvrt        ddd      | j                  j                  t              j	                  t        j
                  |k(  t        j                  | j                  k(        j                         }|st        dt        d      d      ||_
        | j                  j                          dd	d
d}||   }| j                  j                  t              j	                  t        j                  | j                  k(  t        j                  |k(  t        j                  |k(        j                  t        j                   j#                               j                         }|rFt%        | j                  | j                  |j
                  |       | j                  j                          | j&                  j)                  | j*                  dd|| j                  d|i|       |||r|j
                  dS ddS )u+   PDF 대표 이미지 선택 (original/A/B).)originalABzimage.invalid_variantz'Variant must be 'original', 'A', or 'B'  codemessagestatus_codestill.not_found  rt   	variant_a	variant_bzstill.select_variantstillvariantrq   actionresource_typeresource_idr-   detail
ip_addressN)r.   selected_variantprimary_image_id)r   rl   r+   r   r,   r/   r-   rm   r2   r   r   commitr   r.   variant_typeorder_by
created_atdescr8   ro   logrn   )rp   r.   r   ipr   variant_type_maptarget_variant_type
target_imgs           r"   select_variantz$SceneVariationService.select_variant   s    00,A  HHNN:&VJMMX-z/D/DHXHX/XYUW 	
 &+,  ") )3;W.w7 HHNN:&V%%)9)99##x/''+>>
 Xj++0023UW 	 $*:*:JMM8THHOO^^)! ''w' 	 	
 ! '1;

 	
 BF
 	
r!   c                N    t        t        j                        | j                  z  S )uV   프로젝트 루트 디렉토리 (W5 F22 Phase B.4: scene_image_service에서 복제).)r   r   projects_dirrm   )rp   s    r"   _get_project_dirz&SceneVariationService._get_project_dir   s    H))*T-=-===r!   c                   	 |rt        j                  |      nd}|dv r|r	 ddlm} ddlm}  || j                  |
d|j                  t        |dd      t        |dd      	      5   |t        |j                  d
d            t        |j                  dd            t        |j                  dd                   ddd       	 |dk(  rc|ra|j!                  |t        |j                  d
d            t        |j                  dd            t        |j                  dd                  }n|dk(  r|r|j#                  ||      }n|dk(  rf|rd|rb|j%                  |t        |j                  d
d            t        |j                  dd            t        |j                  dd            |      }nt        j                  d||       yt)               }|	| dz  }|j+                  |       t-        d0i d|d| j                  ddd|j                  d|
dt/        |      dd | d!t0        j2                  d"d#d$d%d&|d'|d(|d)|d*|d*   d+|j                  d+      d,t5               }| j6                  j9                  |       t;        |d-d.|r|gng /       | j6                  j=                          t?        |      S # t         j                  $ r d}Y w xY w# 1 sw Y   (xY w# t        $ r!}t        j                  d|       Y d}~Pd}~ww xY w# t        $ r!}t        j'                  d||       Y d}~yd}~ww xY w)1u  단일 variation을 i2i 편집으로 생성 (W5 F22 Phase B.5 이관).

        facade의 generate_scene_with_variations가 본 메서드를 호출한다. 이전에는
        `_generate_variation` internal helper였으나 variation-side 로직으로 분리.
        N)angleangle+colorr   )_create_camera_diagram)generation_contextangle_camera_diagramscene_index
shot_index)stager.   r   r   
horizontalverticalzoom      ?z0B4 camera_diagram capture failed (non-fatal): %sr   )image_bytesr   r   r   color)r   color_promptr   )original_bytesr   r   r   r   z+Variation %s has type=%s but missing paramszVariation %s i2i failed: %s.pngr/   r-   
asset_typescener.   
episode_idrR   prompt_usedi2i_generation_modelstatus	generatedreview_notes r   angle_appliedcolor_appliedsource_image_idcode_versionprompt_file_versionr   scene_stillscene_imagepipeline_roler   input_image_idsr    ) jsonloadsJSONDecodeErrorapp.modules.gemini_i2i_editorr   "app.services.image_capture.contextr   rm   r/   getattrintr=   floatrY   rZ   r[   
edit_angle
edit_coloredit_combinederrorr(   write_bytesr   r   r   gemini_image_modelr#   rl   addr   flushr   )rp   
i2i_editorr   original_idr   variant_labelvar_type
angle_jsonr   	scene_dirr   lineageangle_paramsr   r   _excedited_bytesrg   r4   rR   assets                        r"   _generate_variation_i2iz-SceneVariationService._generate_variation_i2i   s~   &	 5?4::j1TL //LYPQ'$$j8N"XX '}d C&ulDA	 +L,,\1=>L,,Z;<l..vs;<	7"|)44 ."<#3#3L!#DE !1!1*a!@A|//<=	  5   W$)44 .!-  5   ]*|)77#1"<#3#3L!#DE !1!1*a!@A|//<=!-  8   Lm]ef 98*D 11	l+ 

''
 
 XX	

 "
 -Y7
 xj)
 &88
 
 
 '
 %
 '
 (
 !0
  !(,A B!
" v#
& 	U m-8[Mb	
 	U##k ## 	 L	    YQSWXXY6  	LL6sK	s[   K AK/ &AK"<K/ DL KK"K,'K/ /	L8LL	M%MMc                   ddl m} ddlm} | j                  j                  t              j                  t        j                  | j                  k(  t        j                  |k(  t        j                  dk(  t        j                  dk\  t        j                  dk7        j                  t        j                        j                         }|sy| j!                         }|dz  |z  dz  } |       } |t#               t$        j&                  	      }d}	d}
t)        |      D ]  \  }}| j                  j                  t*              j                  t*        j,                  |j.                  k(  t*        j0                  dk(  t*        j2                  d
k(        j                  t*        j4                  j7                               j9                         }|st;        |j<                        }|j?                         s|jA                         }|jB                  s	 |jE                  ||jF                  xs d|jH                  xs d      }|jK                  di       }|jK                  di       }|jK                  dd      |_!        |jK                  d      }|r|jK                  dd      |d<   |rtM        jN                  |      nd|_(        |jK                  d      xs d|_)        |jK                  dd      |_*        |jK                  dd      |_+        |jK                  d      }|r|jK                  dd      |d<   |rtM        jN                  |      nd|_,        |jK                  d      xs d|_-        |jK                  dd      |_.        |jK                  dd
      |_/        | j                  ja                          |	dz  }	tb        je                  d|dz   tg        |      |jK                  d      |jK                  d             t)        d|jB                  |jP                  |jR                  fd|jV                  |jX                  |jZ                  fg      D ]  \  }\  }}}}|r|dk(  r| j                  j                  t*              j                  t*        j,                  |j.                  k(  t*        j2                  |k(        j9                         }|r	 |rtM        jl                  |      nd}|xs d}|r|jo                  dd      nd}|dk(  rI|rG|jq                  ||jK                  dd      |jK                  dd      |jK                  dd      |      }nm|dk(  r|r|js                  ||      }nS|d k(  rL|rJ|rH|ju                  ||jK                  dd      |jK                  dd      |jK                  dd      ||!      }nq|tw                d"z  } | jx                  j{                  dd#       | j}                  |       t+        tw               | j                  d|j.                  |t        |       |j                  xs dt$        j&                  d$||||j.                  dt               %      }!| j                  j                  |!       t        |!d&d'|r|j.                  gng (       | j                  ja                          |
dz  }
tb        je                  d)||dz            tb        je                  d+|	|
       y# th        $ r%}tb        jk                  d|dz   |       Y d}~d}~ww xY w# th        $ r&}tb        jk                  d*||dz   |       Y d}~d}~ww xY w),u%  기존 원본 이미지 기반 A/B 변형 추천 + 생성 (W5 F22 Phase B.4 이관).

        for each still:
          1) variation_a_type/b_type 없으면 V2 recommender로 추천 → DB 저장
          2) 추천된 variation 중 아직 생성 안 된 것 GeminiI2IEditor로 생성
        r   )GeminiI2IEditor)VariationRecommenderV2TstaleNimagesr   )api_keymodelrt   r   )r   scene_descriptionr\   variation_avariation_btypenoner   compositionr   reasonrecommendedr*   z&Variation %d/%d recommended: A=%s B=%sz!Variation recommend %d failed: %sr~   r   r   r   r   r   )promptr   )r   r   r   )parentsexist_okr   )r/   r-   r   r.   r   rR   r   r   r   r   r   r   r   r1   r   r   r   r   z#Variation %s generated for still %dz/Variation %s generation failed for still %d: %sz1Variations complete: recommended=%d, generated=%d)Dr   r   $app.modules.variation_recommender_v2r   rl   r+   r   r,   r-   rm   r   is_selectedstill_indexr   r   r0   r   r   r   r   rS   r   r.   r/   r   r   r   r   r2   r   rR   rT   rV   variation_a_typerecommend_from_imaget2i_prompt_cinematicr\   r=   r   dumpsvariation_a_anglevariation_a_colorvariation_a_reasonvariation_b_typevariation_b_anglevariation_b_colorvariation_b_reasonrecommended_variantr   rZ   inforE   rY   r[   r   popr   r   r   r(   parentmkdirr   r   r   r#   r   r   )"rp   r   r   r   
stills_ormproject_dirr   var_recommenderr   r   r   si	still_ormoriginal_assetoriginal_pathr   recvar_avar_bangle_aangle_brg   var_idx	var_labelvar_type_valvar_angle_valvar_color_valexisting_var
angle_datar   r   editedvar_path	var_assets"                                     r"   generate_variations_onlyz.SceneVariationService.generate_variations_onlyl  s    	BO HHNN:&V%%)9)99%%3&&$.&&!+!!W, Xj,,-SU 	 ++-(*Z7'A	02$ N--


 	&z2MB	 z*''9<<7))W4++z9
 *//4467  " !9!9:M '')*557N --)>>$2*3*H*H*NB#,#7#7#=2 ? C
  GGM26EGGM26E16661JI.#ii0G16="1M.IP$**W2EVZI/27))G2D2LI/3899Xr3JI016661JI.#ii0G16="1M.IP$**W2EVZI/27))G2D2LI/3899Xr3JI047GGM:4VI1HHOO%1$KKK H "QJ69JEIIV\L]_ U^i88):U:UW`WrWrsi88):U:UW`WrWrs_ UPP)\=- $|v'= HHNN:.V"++y||;"//9< UW   :n>KM!:QUJ#0#8DLGQ*.."CWYK#w.:!+!6!6*&NN<;&NN:q9&NN637#. "7 " &0\!+!6!6~|!T%6:,!+!9!9*&NN<;&NN:q9&NN637)5(3 ": " !(gi[+==HOO))$)F((0 *"9#'#3#3#*!*#-"8"B$2$>$>$D")1)D)D*%.&3&3(6(9(9#$#'6!I" HHLL+,!m?M):):(;SU HHOO%NIKK EyRTWXRXYUUk 3F 	GV_`g ! NN#FQPST` ! nNN#TV_acfgagilmmns9   G)[7C.\(<D\(7	\% \  \%(	]1]]c                   ddl m}  |       st        dt        d      d      | j                  j                  t              j                  t        j                  |k(  t        j                  | j                  k(        j                         }|st        dt        d      d      |j                  xs d}|j                  xs d}dd	lm}  |t!        | j                  | j                        
      }|j#                  |j$                  xs d|j&                  xs d||      }	|	j)                  di       }
|
j)                  dd      |_        |
j)                  d      r$t-        j.                  |
j)                  d            nd|_        |
j)                  d      xs d|_        |
j)                  d      xs d|_        |	j)                  di       }|j)                  dd      |_        |j)                  d      r$t-        j.                  |j)                  d            nd|_        |j)                  d      xs d|_        |j)                  d      xs d|_        |	j)                  dd      |_        | j                  jA                          | jB                  jE                  | jF                  dd|| j                  |j*                  |j6                  |j>                  d|       |	S )u=   LLM에게 A/B 변형 추천 받기 (W5 F22 Phase B.3 이관).r   )has_openai_keyzimage.openai_key_missingrw   rx   r|   r}   z{})VariationRecommender)project_llm_configr   )r   r\   camera_anglelighting_moodr   r   r   r   Nr   r   r   r   rt   zstill.recommend_variationsr   )r   r   r   r   )$app.core.openai_keysr  r   r   rl   r+   r   r,   r/   r-   rm   r2   camera_jsonlighting_json!app.modules.variation_recommenderr  r   	recommendstill_frame_promptr\   r=   r   r   r   r   r   r   r   r   r   r   r   r   ro   r   rn   )rp   r.   r   r  r   r  r  r  recommenderresultr  r  s               r"   recommend_variationsz*SceneVariationService.recommend_variations  sw    	8/45  HHNN:&VJMMX-z/D/DHXHX/XYUW 	
 &+,  ((0D++3tJ*6txxAQAQR
 &&#66<"''-2%'	 ' 
 

="-!&66!:DIIIgDV$**UYYw-?"@\`"'))G"4"<#(99X#6#>$ 

="-!&66!:DIIIgDV$**UYYw-?"@\`"'))G"4"<#(99X#6#>$ $*JJ}j$I!^^/! ''$)$:$:$)$:$:$88
  	 	
 r!   c                   t         j                  s$t               dk(  rt        dt	        d      d      | j
                  j                  t              j                  t        j                  |k(  t        j                  | j                  k(        j                         }|st        dt	        d      d      |j                  xs |}| j
                  j                  t              j                  t        j                  |k(  t        j                  | j                  k(        j                         }|st        dt	        d      d      t        |j                        }|j!                         st        dt	        d      d      |j#                         }	d}
|r	 t%        j&                  |      }
|
st        dd	d      t         j*                  rt         j,                  st        d
dd      t/        |
j1                  dd            }t/        |
j1                  dd            }t/        |
j1                  dd            }t3        |	|||      \  }}|st        ddd      t4        j7                  d||||       t        |j                        j8                  }t;               }|| dz  }|j=                  |       ddlm }  ||t        |j                        jB                  tE        |      tE        |      tE        |      dtG               d       t        || j                  d|jH                  |jJ                  tM        |      d| d| d| dddd||tG                     }| j
                  jO                  |       tQ        |d d!|r|gng "       | j
                  jS                          | jT                  jW                  | jX                  d#d$|| j                  |d%d&|'       t[        |      S # t$        j(                  $ r Y Uw xY w)(u  원본 기반 fal.ai i2i로 변형 재생성 (W5 F22 Phase B.2 이관).

        현재 구현: angle_json(horizontal/vertical/zoom) 기반 **앵글만** 편집.
        color_prompt 파라미터는 reserved — color-only 호출은 image.no_edit_params 400.
        r   zimage.gemini_key_missingrw   rx   image.not_foundr}   Nzimage.no_edit_paramszNo angle parameters providedzimage.fal_key_missingu.   fal.ai API 키가 설정되지 않았습니다r   r   r   r   zimage.fal_failedu*   fal.ai 앵글 편집에 실패했습니다i  z/fal.ai angle edit: H=%.0f V=%.0f Z=%.0f (%d ms)r   )embed_png_metadataz+fal-ai/qwen-image-edit-2511-multiple-angles)source_imageangle_horizontalangle_vertical
angle_zoomr   r   r   z[fal.ai angle] H=z V=z Z=r   r   	angle_fal)r/   r-   r   r.   r   rR   r   r   r   r   r   r   r   r   scene_angle_falr   r   zimage.regenerate_variationimage	fal_angle)r   	edit_typer   ).r   gemini_api_keygemini_key_countr   r   rl   r+   r   r,   r/   r-   rm   r2   r   r   rR   rT   rV   r   r   r   fal_ai_enabledfal_keyr   r=   _apply_fal_anglerZ   r   r   r(   r   app.modules.png_metadatar'  namer%   r#   r.   r   r   r   r   r   ro   r   rn   r   )rp   r4   r   r   r   img	source_id
source_imgsource_pathr   r   hvz	fal_bytesfal_elapsedr   new_idrR   r'  r   s                        r"   regenerate_variationz*SceneVariationService.regenerate_variation_  s    &&+;+=+B/45  HHNN:&V)%%)9)99 UW 	 &+,  ''38	HHNN:&VJMMY.
0E0EIYIY0YZUW 	
 &+,  ://0!!#&+,  %//1#zz*5
 +6  &&h.>.>,H  ,""<34,"":q12,""63/0!1.!Q!J	;'D  	Eq!QP[\ --.55	6($/	i( 	@9 !5!56;; #A!!fa&B&'
 	 ''\\~~,Y7+A3c!Cs;J$$%v
  	U !2-+4YK"	
 	^^/!''#,(  	 	
 U##o '' s   O. .PPc           	     ,   | j                   j                  t              j                  t        j                  |k(  t        j
                  | j                  k(        j                         }|st        dt        d      d      | j                   j                  t              j                  t        j                  |k(  t        j
                  | j                  k(  t        j                  |k(        j                         }|st        dt        d      d      t        | j                   | j                  ||       | j                   j                          | j                  j                  | j                   dd|| j                  d|i|       t#        |      S )	u)   원본 이미지 선택 (여러 장 중).r|   r}   rx   r&  zstill.select_originalr   r4   r   )rl   r+   r   r,   r/   r-   rm   r2   r   r   r   r.   r8   r   ro   r   rn   r   )rp   r.   r4   r   r   r8  s         r"   select_originalz%SceneVariationService.select_original  sP    HHNN:&VJMMX-z/D/DHXHX/XYUW 	
 &+,  HHNN:&V)%%)9)99##x/
 UW 	 &+,  	DHHd&6&6(K^^*! '') 	 	
 S!!r!   )r3   
OrmSessionr-   r%   rq   r%   returnNoner   )r.   r%   r   r%   r   Optional[str]rF  Dict[str, Any])rF  r   )r   r   r   bytesr   r%   r   r   r   r%   r   r%   r   rH  r   rH  r   r   r   r%   r   rI  rF  zOptional[Dict[str, Any]])r   r%   rF  rG  )r.   r%   r   rH  rF  rI  )NNN)
r4   r%   r   rH  r   rH  r   rH  rF  rI  )r.   r%   r4   r%   r   rH  rF  rI  )__name__
__module____qualname____doc__rr   r   r   r   r  r$  rB  rD  r    r!   r"   rj   rj      s   K	*	* 	* 		*
 
	* !	=
=
 =
 	=

 
=
~>j$j$ j$ 	j$
 j$ j$ j$ "j$ $j$ j$ j$  j$ 
"j$XhaZ !GG G 
	GX %)&* V$V$ "V$ $	V$
 V$ 
V$x !	0"0" 0" 	0"
 
0"r!   rj   )rF  r%   )
r3   rE  r-   r%   r4   r%   r.   r%   rF  rG  )
rH   List[Dict[str, Any]]rI   r   rJ   r   rK   boolrF  z''tuple[int, Optional[tuple[int, int]]]')rH   rO  r\   r%   rF  z'tuple[int, bool]')7rN  
__future__r   r   loggingr&   r   r   pathlibr   typingr   r   r	   r
   sqlalchemy.ormr   rE  app.core.configr   app.core.errorsr   app.core.file_pathsr   #app.services.image_capture.annotater   app.i18n.loaderr   app.logging.activity_loggerr   app.models.projectr   r   app.modules.llm.gemini_key_poolr   r2  r   app.services.fal_angle_helpersr   r5  r   rW   "app.services.image_service_helpersr   r   r   	getLoggerrK  rZ   r#   r(   r8   rP   rh   rj   r    r!   r"   <module>ra     s    #    '  , , 0 $ $ 6 H  6 5 W  
		8	$2  	
 
8/*// / 	/
 -/d)"*)")" )"Xx	" x	"r!   