
    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ZddlZ	ddl
Z	ddlmZ ddlmZ ddlmZmZ ddlmZ  ej(                  e      Zg dZ	 	 d	 	 	 	 	 	 	 	 	 	 	 dd	Z	 	 	 	 	 	 dd
Z	 	 	 	 	 	 	 	 	 	 ddZy)u  fal.ai 앵글 helper — Phase 3b.5에서 image_service.py에서 이관.

세 개의 module-level 함수를 별도 모듈로 분리해 `scene_image_service.py` →
`image_service.py` 역의존을 제거한다. ImageService에서는 backward compat 용도로
동일 이름의 shim을 유지한다.

- `select_and_recommend_angle` (구 `_select_and_recommend_angle`)
- `select_final_best`         (구 `_select_final_best`)
- `apply_fal_angle`           (구 `_apply_fal_angle`)
    )annotationsN)Optional)settings)ImageCallBudgetExceededreserve_current_call)capture_generated_image)select_and_recommend_angleselect_final_bestapply_fal_anglec                Z   ddl m}m} | syt        |       }d}|rd| }|r|d| z  }d| d| d	| | d
|dz
   d
}		 d|	dg}
t	        |       D ]\  \  }}t        j                  |      j                         }|
j                  dd|dz    dd       |
j                  ddd| id       ^  |dd|
dgddi |d            }t        j                  |j                  d   j                  j                        }t        dt        |dz
  t!        |j#                  dd                        }t        dt        dt%        |j#                  dd                        }t        dt        d t%        |j#                  d!d                        }t        dt        d"t%        |j#                  d#d$                        }|d%k  r(t'        |      d%k  r|t'        |      k\  rd&}n	|dk\  rd&nd'}|||||j#                  d(d      d)S # t(        $ r }t*        j-                  d*|       Y d}~yd}~ww xY w)+u   GPT Vision: N개 이미지 중 앵글 적용할 이미지 선택 + 앵글 추천 (최소 20도).

    LiteLLM Router 경유 — Opik 자동 추적.
    prev_scene_title/description: 앞쪽 씬 텍스트 맥락 (이미지 아닌 텍스트만)
    r   build_call_metadatarouter_completionN z*

Previous scene (for visual continuity): 
z$You are a cinematographer. You have z0 candidate images for the current scene.
Scene: z	
Prompt: a  

Task:
1. Consider visual continuity with the previous scene description.
2. Choose which image would benefit MOST from a camera angle adjustment.
3. Recommend the camera angle adjustment for that image.

Rules:
- horizontal_angle: 0-360 (0=front, 90=right side, 180=back, 270=left side)
- vertical_angle: -30 to 90 (-30=low angle looking up, 0=eye level, 90=bird's eye)
- zoom: 0-10 (0=wide shot, 5=normal, 10=extreme closeup)
- IMPORTANT: horizontal_angle >= 20 OR abs(vertical_angle) >= 20. No trivial adjustments.

Return JSON:
{"best_for_angle": N, "horizontal_angle": N, "vertical_angle": N, "zoom": N, "reason": "Korean explanation"}
best_for_angle is 0-based index (0 to    ).texttyper   Image :	image_urlurldata:image/png;base64,r   r   gptuserrolecontentr   json_objectangle_recommendmodelmessagesresponse_formatmetadatabest_for_angleih  horizontal_angleiZ   vertical_angle
   zoom      g      4@g      4reason)r)   r*   r,   r.   r1   z)Angle selection+recommendation failed: %s)app.modules.llm.llm_clientr   r   len	enumeratebase64	b64encodedecodeappendjsonloadschoicesmessager!   maxminintgetfloatabs	Exceptionloggerwarning)image_bytes_list
beat_title
t2i_promptprev_scene_titleprev_scene_descriptionr   r   nprev_contextpromptr!   i	img_bytesimg_b64responseresultidxhvzexcs                        T/Users/manta/Documents/Projects/TheRoad-I1/backend/app/services/fal_angle_helpers.pyr	   r	   '   s\   ? ALEFVEWX!b!7 899L5aS 9| 		\N #' ()se2#1F&"F34%&67LAy&&y188:GNNFfQqSEODENNKuH^_f^gFh>ijk 8
 %%':;#]3():;
 H,,Q/77??@!SQFJJ/?$C DEF3sE&**-?"CDEFSU6::.>#BCDE3r5FA!6789r6c!frkCF{FD"%1PQ[\hnhrhrs{}  iA  B  	B BCHs   GH 	H*
H%%H*c           	        ddl m}m} | syt        |       }|dk(  ryd| d| d| d}	 d|d	g}t	        |       D ]\  \  }}t        j                  |      j                         }	|j                  dd
|dz    dd	       |j                  ddd|	 id       ^  |dd|dgddi |d            }
t        j                  |
j                  d   j                  j                        }t        |j                  dd            }t!        dt#        |dz
  |dz
              }t$        j'                  d|||j                  dd             |S # t(        $ r }t$        j+                  d|       Y d}~yd}~ww xY w)ux   GPT Vision: N+1개 이미지 중 최종 대표 이미지 선택.

    LiteLLM Router 경유 — Opik 자동 추적.
    r   r   r   zMYou are a film director selecting the final hero image for a scene.
You have zA images. The last image may be an angle-adjusted variant.
Scene: aR  

Select the BEST image that:
1. Has the most cinematic composition and visual impact
2. Best represents the scene's mood and narrative moment
3. Has good lighting, focus, and overall quality
4. Feels like a professional film still frame

Return JSON: {"selected_index": N, "reason": "Korean explanation"}
selected_index is 1-based (1 to r   r   r   r   r   r   r   r   r   r   r   r   r   r"   final_selectr$   selected_indexu(   Final best selection: image %d/%d — %sr1   r   z4Final best selection failed, defaulting to first: %sN)r2   r   r   r3   r4   r5   r6   r7   r8   r9   r:   r;   r<   r!   r?   r@   r=   r>   rD   inforC   rE   )rF   rG   r   r   rK   rM   r!   rN   rO   rP   rQ   rR   selectedrS   rW   s                  rX   r
   r
   u   s   ? AAv

 | 	! "#2)F"F34%&67LAy&&y188:GNNFfQqSEODENNKuH^_f^gFh>ijk 8
 %%':;#]3(8	
 H,,Q/77??@vzz"2A67!SAq1u-.>!VZZX`bdMef
 MsSs   DE	 		E2E--E2c                   t        |       dkD  r	 ddlm} |j                  t	        j
                  |             }d}t        ||j                  z  ||j                  z        }|dk  rLt        |j                  |z        t        |j                  |z        f}|j                  ||j                        }t	        j
                         }	|j                  |	d       |	j                         } t#        j$                  |       j'                         }d
| g|||ddd}t(        j*                  j-                  dt/        j0                  |      j3                         dt4        j6                   dd      }t9        d       t;        j:                         }	 t(        j*                  j=                  |d      5 }t/        j>                  |jA                               }d	d	d	       t        t;        j:                         |z
  dz        }jC                  dg       }|sy|d   jC                  dd      }|syt(        j*                  j=                  |d      5 }|jA                         }d	d	d	       ddl"m#}  |       jI                  ddd| d | d!| ||||d"#       tK        dd| d | d!| |||dd$%       ||fS # t        $ r!}
t        j!                  d|
       Y d	}
~
d	}
~
ww xY w# 1 sw Y   xY w# 1 sw Y   xY w# tL        $ r  t        $ rX}
t        j!                  d&|
       ddl"m#}  |       jI                  ddd| d | d!| d'tO        |
      |||d"(       Y d	}
~
yd	}
~
ww xY w))uV   fal.ai로 이미지에 앵글 적용. Returns (result_bytes, elapsed_ms) or (None, 0).i D r   )Imagei   r   PNG)formatuD   fal image resize optimization failed: %s — 원본 바이트 사용Nr   png)
image_urlsr*   r,   r.   output_format
num_imagesz;https://fal.run/fal-ai/qwen-image-edit-2511-multiple-angleszKey zapplication/json)AuthorizationzContent-Type)dataheaderszfal_angle_helpers.fal_run)sourcex   )timeouti  images)Nr   r   r      )get_image_tracer	fal_anglezqwen-image-edit-2511zH=z V=z Z=)
horizontalverticalr.   )stepr%   rM   duration_msparams)rp   rq   r.   budget_source)r    rM   pipeline_metadatazfal.ai angle apply failed: %serror)rr   r%   rM   statusrw   rt   )(r3   PILr_   openioBytesIOr>   widthheightr?   resizeLANCZOSsavegetvaluerC   rD   rE   r5   r6   r7   urllibrequestRequestr9   dumpsencoder   fal_keyr   timeurlopenr:   readr@   app.modules.llm.image_tracerrn   logr   r   str)rO   rp   rq   r.   r_   pil_imgmax_siderationew_sizebufrW   rP   bodyreqt0resprR   elapsedrl   
result_urldlrg   rn   s                          rX   r   r      s=    9~	!	h!jjI!67GH7==0(W^^2KLEqy 56GNNU<R8ST!..5==A**,CLLUL+I y)002G 0y9:&"D ..
 
 EZZ$$&#H$4$4#56.
 ! C  ;<	B/^^##C#5ZZ		,F 6tyy{R'4/0Hb)AY]]5"-
^^##J#;r779D <A$:
|3xjD6:",(DQ	 	 	
 	 
|3xjD6:($!<		
	
 W}C  	hNNacfgg	h> 65 <;. #  	6<A$:
|3xjD6:#c(",(DQ	 	 	
 	sn   CJ8 !K> 3$K%?K> K> /!K> K2!AK> 8	K"KK"%K/*K> 2K;7K> >M(AM##M()r   r   )rF   listrG   r   rH   r   rI   r   rJ   r   returnzOptional[dict])rF   r   rG   r   r   r?   )
rO   bytesrp   rA   rq   rA   r.   rA   r   tuple)__doc__
__future__r   r5   r{   r9   loggingr   urllib.errorr   urllib.requesttypingr   app.core.configr   app.core.image_call_budgetr   r   app.services.image_capture.sinkr   	getLogger__name__rD   __all__r	   r
   r        rX   <module>r      s   	 #  	       $ D			8	$ "$KKK K 	K
  K K\333 	3laaa a 	a
 ar   