
    (<iP                        U 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 ddlmZ ddl	m
Z
mZ ddlZddlZ ed      Z ed      Zedz  Zd	d
ddZeeef   ed<   ddddZeeef   ed<   ddddZeeef   ed<   dedeeef   fdZdedeeee
f      fdZdedeeee
f      ddfdZdeded ed!edeee
f   f
d"Zd#Zd$Zdeded!edeee
f   fd%Zded!edeee
f   fd&Z deded!edeee
f   fd'Z!	 	 d2d(ed)ed ed*eeef   d+ee   d,edeee
f   fd-Z"dejF                  fd.Z$d*eeef   dee   fd/Z%d3d0Z&e'd1k(  r e&        yy)4u   AI 이미지 생성 스크립트 - GPT Image 1 (OpenAI)과 Gemini Imagen (Google) API를 사용하여
보험 GA 리크루팅 광고 이미지를 생성하고 결과를 JSON 메타데이터로 저장합니다.    N)datetime)Path)AnyOptionalz-/home/jay/workspace/tools/ai-image-gen/outputz/home/jay/workspace/.env.keysresults.jsongI+?g/$?gK7A`?lowmediumhigh
COST_TABLEu?  Professional Korean insurance general agency recruiting advertisement. Modern office environment with confident business professionals. Clean, premium design. 1080x1080 square format for Meta ads. Text overlay: '당신의 커리어, 새로운 시작' in Korean. Color scheme: navy blue and gold. Corporate premium feel.u   Korean insurance consultant personal branding image. Confident male professional in business suit, modern Seoul office backdrop. Warm lighting, trustworthy atmosphere. 1080x1080 square. Subtle text: '보험의 모든 것' in elegant Korean typography.u   Motivational career change advertisement for insurance professionals. Split image: left side dark/stressful office, right side bright/modern workspace. Aspirational feel. 1080x1080 square format. Korean text: '지금이 기회입니다' centered.ABC	SCENARIOSu  Professional advertisement photograph for Korean insurance general agency recruitment. Cinematic composition using rule of thirds with strong leading lines. Environment: luxurious 100th-floor Seoul panoramic office with floor-to-ceiling glass windows revealing city skyline at golden hour, minimalist interior with warm natural wood accents and a single elegant desk. Subject: Korean male professional in his early 30s wearing a perfectly tailored navy double-breasted suit with subtle gold cufflinks, confident subtle smile, chin slightly lifted at 15 degrees, 3/4 angle portrait. Lighting: natural golden-hour backlight streaming through windows creating beautiful rim lighting on subject's silhouette, soft diffused fill light illuminating face evenly, subtle volumetric light rays in the air. Color palette: deep navy (#1B2A4A), warm gold (#C9A84C), soft cream highlights, muted charcoal accents. Shot on Phase One IQ4 150MP with 80mm lens, f/4 aperture for perfect subject isolation, 8K resolution. Professional advertising photography quality. Premium aspirational corporate recruitment campaign style. Bold headline text '새로운 시작' in white sans-serif font, top-center. No stock photo aesthetic, no clipart, no watermark, no generic corporate imagery. Photorealistic.u  Premium personal branding photograph for elite Korean insurance consultant. Golden ratio composition with shallow depth of field. Environment: high-end private consultation room with rich dark walnut executive desk, neatly arranged premium leather portfolio and Mont Blanc pen, warm ambient bookshelf with curated books in soft background bokeh. Subject: Korean male professional in his 40s wearing charcoal business suit, thin titanium-framed glasses, warm genuine smile conveying trust and expertise, 3/4 frontal angle, one hand gently resting on desk surface. Lighting: warm key light from 45 degrees with large softbox creating gentle shadows, subtle fill light at 1:3 ratio, warm amber ambient glow from background. Color theory: analogous warm palette — rich amber (#D4A574), cream (#FFF8E7), dark walnut brown (#3C2415), touch of burgundy (#722F37). Shot on Hasselblad H6D-100c medium format, 100mm f/2.8 lens for creamy bokeh and subject isolation. Style: premium financial advisor personal branding campaign, like a Rolex or private banking advertisement. No text overlay — focus purely on image quality and emotional impact. No stock photo feel, no clipart, no watermark. Photorealistic, warm and trustworthy atmosphere.u  Cinematic metaphorical scene for insurance career transition advertisement. Dramatic wide-angle composition with strong leading lines converging toward bright focal point. Single powerful scene: silhouette of a Korean professional in business attire walking purposefully from a dark, cold-toned corporate corridor toward a luminous warm doorway. Through the doorway: vision of a modern, sunlit co-working space with floor-to-ceiling windows and lush green plants. Lighting: dramatic volumetric god-rays streaming through the doorway, high contrast between cool blue-grey shadows (#2C3E50) in the corridor and warm golden light (#F4D03F) from the future. Subtle lens flare from the bright opening. Floor reflections adding depth. Color grading: teal-and-orange cinematic palette, cool shadows transitioning to warm highlights. Shot on ARRI Alexa 65 with Panavision anamorphic 40mm lens, 6K cinema quality, 2.39:1 aspect ratio feel within square frame. Style: like a Christopher Nolan or Denis Villeneuve film still — epic, contemplative, transformative. Single line of Korean text '지금, 당신의 차례' in clean white sans-serif, bottom-center, small size. No stock photo aesthetic, no watermark. Photorealistic, deeply aspirational mood.V2_SCENARIOSfilepathreturnc                    i }| j                         st        d|         |S t        | d      5 }|D ]  }|j                         }|r|j	                  d      r'|j	                  d      r|t        d      d }d|v sK|j                  d      \  }}}|j                         j                  d      }|||j                         <    	 ddd       |S # 1 sw Y   |S xY w)	uE   환경변수 파일에서 KEY=VALUE 형식의 키를 로드합니다.u:   [WARNING] 환경변수 파일을 찾을 수 없습니다: utf-8encoding#zexport N=z"')existsprintopenstrip
startswithlen	partition)r   keysflinek_vs          U/home/jay/workspace/.worktrees/task-2057-dev2/tools/ai-image-gen/generate_ai_image.pyload_env_keysr)   a   s    D??J8*UV	h	) $Q 
	$D::<D4??3/y)C	N,-d{..-1aGGIOOE*"#QWWY
	$$ K$ Ks   AC>A	CCresults_filec                     | j                         sg S t        | d      5 }t        j                  |      cddd       S # 1 sw Y   yxY w)u'   기존 results.json을 로드합니다.r   r   N)r   r   jsonload)r*   r#   s     r(   load_resultsr.   v   s?     		lW	- yy|  s	   ?Aresultsc                 |    t        | dd      5 }t        j                  ||dd       ddd       y# 1 sw Y   yxY w)u*   results.json에 결과를 저장합니다.wr   r   F   )ensure_asciiindentN)r   r,   dump)r*   r/   r#   s      r(   save_resultsr6   ~   s7    	lC'	2 <a		'15;< < <s   2;api_keypromptqualityoutput_pathc           	      0   d}d|  dd}d|dd|d}t        d	| d
       t        j                         }t        j                  |||d      }|j	                          t        j                         |z
  }	|j                         }
|
d   d   d   }t        j                  |      }|j                  |       |j                         j                  }t        d|j                   d|dd|	dd       dt        |	d      |t        j                  |d      ddS )uJ   GPT Image 1 API를 호출하여 이미지를 생성하고 저장합니다.z,https://api.openai.com/v1/images/generationsBearer application/jsonAuthorizationzContent-Typezgpt-image-1   	1024x1024)modelr8   nsizer9   u.   [GPT] 이미지 생성 요청 중... (quality=)x   headersr,   timeoutdatar   b64_jsonu   [GPT] 생성 완료:  (, bytes, .1f   초)gptr2   g        N)apitime_secondsfile_size_bytescost_usderror)r   timerequestspostraise_for_statusr,   base64	b64decodewrite_bytesstatst_sizenameroundr   get)r7   r8   r9   r:   urlrH   payload
start_timeresponseelapsedrJ   	image_b64image_bytes	file_sizes                 r(   generate_gpt_imagerk      s-    9C"7),*G
 G 
:7)1
EFJ}}S'MHiikJ&G==?D &\!_Z0I""9-KK(  "**I	!+"2"2!32i](7SV-W[
\] gq)$NN7C0     z3https://www.googleapis.com/auth/generative-languagezimagen-3.0-generate-002c           	         ddl m} |j                  |       }t        dt         d       t        j
                         }|j                  j                  t        ||j                  j                  dd      	      }t        j
                         |z
  }|j                  st        d
      |j                  d   }|j                  t        d      |j                  j                  t        |             |j                         j                   }	t        d|j"                   d|	dd|dd       ddt%        |d      |	dddS )uJ   API 키를 사용하여 Gemini Imagen API로 이미지를 생성합니다.r   )genai)r7   uF   [Gemini] API 키 인증으로 이미지 생성 요청 중... (모델: rE   r@   z	image/png)number_of_imagesoutput_mime_type)rB   r8   configu8   Gemini API가 이미지를 반환하지 않았습니다.Nu.   생성된 이미지 데이터가 없습니다.u   [Gemini] 생성 완료: rL   rM   rN   rO   rP   geminir7   r2   rR   auth_methodrS   rT   rU   rV   )googlern   Clientr   _GEMINI_IMAGEN_MODELrW   modelsgenerate_imagestypesGenerateImagesConfiggenerated_imagesRuntimeErrorimagesavestrr^   r_   r`   ra   )
r7   r8   r:   rn   clientre   rf   rg   	generatedrj   s
             r(   #_generate_gemini_image_with_api_keyr      s;    \\'\*F	RSgRhhi
jkJ}},,"{{//( 0 
 - H iikJ&G$$UVV))!,IKLLOO[)*  "**I	$[%5%5$6b1XgVY]Z^
_`  gq)$ rl   c           	         t        j                  t              }d}d}| d| d}d| dd}dd	| igigd
ddgid}t        d| d       t	        j                         }t        j                  |||d      }	|	j                          t	        j                         |z
  }
|	j                         }|j                  dg       }|st        d      |d   j                  di       j                  dg       }d}|D ]
  }d|v s|} n |t        d      |d   j                  dd      }t        j                  |d   d         }d|v rdnd}|j                  j                         |k7  r|j                  |      }|j!                  |       |j#                         j$                  }t        d|j&                   d |d!d"|
d#d$       d%d&t)        |
d'      |ddd(S ))ua   SA Bearer 토큰을 사용하여 Gemini generateContent REST API로 이미지를 생성합니다.z0https://generativelanguage.googleapis.com/v1betazgemini-3.1-flash-image-previewz/models/z:generateContentr<   r=   r>   partstextresponseModalitiesIMAGETEXT)contentsgenerationConfiguJ   [Gemini] SA 토큰 fallback으로 이미지 생성 요청 중... (모델: rE      rG   
candidatesu1   SA fallback 응답에 candidates가 없습니다.r   contentN
inlineDatau:   SA fallback 응답에 이미지 데이터가 없습니다.mimeTypez
image/jpegrJ   jpegz.jpg.pngu$   [Gemini] SA fallback 생성 완료: rL   rM   rN   rO   rP   rr   sa_bearer_token_fallbackr2   rs   )gcloud_authget_service_account_token_GEMINI_SCOPEr   rW   rX   rY   rZ   r,   rb   r}   r[   r\   suffixlowerwith_suffixr]   r^   r_   r`   ra   )r8   r:   tokengemini_api_basemodel_idrc   rH   rd   re   rf   rg   rJ   r   r   
image_partpart	mime_typeri   extrj   s                       r(   $_generate_gemini_image_with_sa_tokenr      s#   
 11-@EHO 0HXhZ/?
@C"5'**G
  01231GV3DEG
 
VW_V``a
bcJ}}S'MHiikJ&G#==?D,+JNPPqMi,00"=E(,J 4J
 WXX-11*lKI"":l#;F#CDKi'&VC!S(!--c2K(  "**I	01A1A0B"YqMQYZabeYffj
kl 1gq)$ rl   c                 n   | r	 t        | ||      S t        ||      S # t        j                  $ rN}|j                  |j                  j                  nd}|dk(  rt        d       nt        d| d       Y d}~hd}~wt        $ r/}t        dt        |      j                   d| d	       Y d}~d}~ww xY w)
u  Gemini 이미지를 생성합니다.

    인증 우선순위:
    1. GEMINI_API_KEY가 있으면 API 키 인증 (기존 방식)
    2. API 키 실패(429 할당량 초과 등) 시 SA Bearer 토큰으로 fallback

    Args:
        api_key: Gemini API 키 (빈 문자열이면 SA 토큰으로 직행).
        prompt: 이미지 생성 프롬프트.
        output_path: 저장할 파일 경로.

    Returns:
        생성 결과 메타데이터 딕셔너리.
    Nr   i  uP   [Gemini] API 키 할당량 초과 (429). SA Bearer 토큰 fallback 시도 중...u%   [Gemini] API 키 인증 HTTP 오류 (u*   ). SA Bearer 토큰 fallback 시도 중...u    [Gemini] API 키 인증 실패: : u)   . SA Bearer 토큰 fallback 시도 중...)
r   rX   	HTTPErrorrf   status_coder   	Exceptiontype__name__r   )r7   r8   r:   estatuss        r(   generate_gemini_imager      s    & 		w6wTT 0DD !! 	r/0zz/EQZZ++1F}hj=fXEopq 	w4T!W5E5E4FbKtuvv	ws!    B4AA99B4%B//B4rR   scenarioapi_keys
output_dirprompt_versionc                    |t         }|dk(  rt        nt        }||   }|  d| d| d}||z  }	t        j                         j                         }
| |||||
ddddd
}	 | dk(  r5|j                  dd      }|st        d	       d
|d<   |S t        ||||	      }n<| dk(  r-|j                  dd      }|st        d       t        |||	      }n
d|  |d<   |S |j                  |       |S # t        j                  $ rQ}d|j                  j                   d|j                  j                  dd  }t        d|        ||d<   Y d}~|S d}~wt         $ r8}t#        |      j$                   d| }t        d|        ||d<   Y d}~|S d}~ww xY w)u2   단일 이미지 생성 작업을 실행합니다.Nv2r&   r   )
rR   r   r9   r   filename	timestamprS   rT   rU   rV   rQ   OPENAI_API_KEY u>   [WARNING] OPENAI_API_KEY가 없어 GPT API를 스킵합니다.zOPENAI_API_KEY not foundrV   rr   GEMINI_API_KEYuQ   [WARNING] GEMINI_API_KEY 없음. SA Bearer 토큰 fallback으로 시도합니다.zUnknown API: u   HTTP 오류: z -    z[ERROR] r   )
OUTPUT_DIRr   r   r   now	isoformatrb   r   rk   r   updaterX   r   rf   r   r   r   r   r   )rR   r   r9   r   r   r   scenarios_mapr8   r   r:   r   resultr7   
gen_resultr   	error_msgs                   r(   run_generationr   C  s    
$2d$:L	M8$Faz7)40Hx'K((*I (F$%<ll#3R8GVW"<w+GVWkRJH_ll#3R8Gij.wLJ !.cU3F7OMj! M  $#AJJ$:$:#;3qzztPS?T>UV	$%#w M  $Aw''(1#.	$%#wM$s2   *C( 
A
C( C( (F;AEF-FFc                  N   t        j                  dt         j                  d      } | j                  dddgd       | j                  d	g d
d       | j                  dg ddd       | j                  dddd       | j                  dddgddd       | j	                         S )u   CLI 인자를 파싱합니다.uJ   GPT Image 1 / Gemini Imagen을 사용한 보험 GA 광고 이미지 생성uW  
예시:
  python3 generate_ai_image.py --api gpt --scenario A --quality high
  python3 generate_ai_image.py --api gemini --scenario B --quality medium
  python3 generate_ai_image.py --api gpt --scenario A --quality high --prompt-version v2
  python3 generate_ai_image.py --all
  python3 generate_ai_image.py --all --prompt-version v2
        )descriptionformatter_classepilogz--apirQ   rr   u!   사용할 API (gpt 또는 gemini))choiceshelpz
--scenarior   uU   시나리오 선택 (A: GA리크루팅, B: 전문성브랜딩, C: 감성전직유도)z	--qualityr   r
   u=   이미지 품질 (기본값: medium, GPT 전용 파라미터))r   defaultr   z--all
store_truerun_allu<   모든 시나리오를 사용 가능한 모든 API로 실행)actiondestr   z--prompt-versionv1r   r   u*   프롬프트 버전 선택 (기본값: v1))r   r   r   r   )argparseArgumentParserRawDescriptionHelpFormatteradd_argument
parse_args)parsers    r(   r   r     s    $$` <<F !0  
 d  
 )L	   K	   t9   rl   c                     g }| j                  d      r|j                  d       nt        d       | j                  d      r|j                  d       |S t        d       |j                  d       |S )u/   사용 가능한 API 목록을 반환합니다.r   rQ   uI   [WARNING] OPENAI_API_KEY가 없어 GPT API를 사용할 수 없습니다.r   rr   uN   [WARNING] GEMINI_API_KEY 없음. SA Bearer 토큰 fallback을 시도합니다.)rb   appendr   )r   	availables     r(   get_available_apisr     sm    I||$%YZ||$%"  	^_"rl   c                  r   t               } | j                  s.| j                  | j                  t	        d       t        d      | j                  dk(  r
t        dz  }n	t        dz  }|j                  dd       |d	z  }t        t              }d
D ])  }t        j                  j                  |      }|s%|||<   + g }| j                  rYt        |      }|st	        d       t        d      |D ]0  }dD ])  }	|dk(  r| j                  nd}
|j!                  ||	|
f       + 2 n2|j!                  | j                  | j                  | j                  f       t	        dt#        |       d| j                   d       t%        |      }t'        |d      D ]  \  }\  }}	}
t	        d| dt#        |       d| d|	 d|
 
       t)        ||	|
||| j                        }|j!                  |       t+        ||       t	        d|j                  dd       d|j                  dd       d        t	        d|        y)u   메인 실행 함수.NuW   [ERROR] --all 옵션이 없으면 --api와 --scenario를 모두 지정해야 합니다.r@   r   zv2-gpt-advancedz
v1-gpt-pocT)parentsexist_okr   )r   r   u-   [ERROR] 사용 가능한 API가 없습니다.r   rQ   r
   u   
총 uF   개 이미지 생성 작업을 시작합니다. (프롬프트 버전: z)
[/z] API=u   , 시나리오=u	   , 품질=)r   r   u     -> 결과 저장: r   zN/Au
    (오류: rV   u   없음u%   모든 작업 완료. 결과 저장: )r   r   rR   r   r   
SystemExitr   r   mkdirr)   ENV_KEYS_FILEosenvironrb   r   r9   r   r    r.   	enumerater   r6   )argsactive_output_diractive_results_jsonr   keyenv_valtasksavailable_apisrR   r   r9   r/   ir   s                 r(   mainr     sJ   <D <<TXX-1Fghm d"&)::&5D48+n< ]+H3 $**..%#HSM$ )+E||+H5ABQ-! 	7C+ 7*-,$,,Hc8W567	7
 	dhht||<=	F3u:,deiexexdyy|
}~./G'0': v##C7!Ac%j\uOH:YwiXY(..
 	v('2$VZZ
E%B$C>RXR\R\]dfnRoQppstuv 
12E1F
GHrl   __main__)Nr   )r   N)(__doc__r   r[   r,   r   rW   r   pathlibr   typingr   r   r   rX   r   r   RESULTS_JSONr   dictr   float__annotations__r   r   r)   listr.   r6   rk   r   rw   r   r   r   r   	Namespacer   r   r   r    rl   r(   <module>r      s  n    	       AB
45N*  
De 	D	L	=	4S> 0	l	j	\A. d38n .bD T#s(^ *t T#s(^(< <t <d4S>.B <t <+++ + 	+
 
#s(^+\ F0 ))) ) 
#s(^	)X;;; 
#s(^;| E E E  E 
#s(^	 EP "&=	== = 38n	=
 = = 
#s(^=@+H&& +\c3h DI ;I| zF rl   