
    FjL                         U d Z ddlmZ ddlZddlZddlZddlmZ ddlm	Z	m
Z
mZmZmZ ddlmZ ddlmZ  ej$                  e      Zdd	d
dZded<   dZddZ G d de      Zy)u   gpt-image-2.5 클라이언트 — nb2 gen_fn 슬롯·SAFETY 사다리 호환.

2026-09-20 사용자 결정: 최종 스틸 **조립**을 gpt-image-2.5 로 간다.
실측 근거 — nb2 가 두 판 이상 못 고치던 샷 8개를 같은 문안·같은 참조로
gpt-image-2.5 에 넘기니 **여덟 장 다 한 판**에 해결됐다
(`project-gpt25-direct-assembly-beats-nb2-rerolls`).

★왜 gen_fn 이 아니라 **클라이언트**인가. `gpt_image_gen.make_gpt_image_gen_fn`
 이 이미 있지만 그것은 「연화 1회」뿐이다. 사용자가 지시한 검열 사다리는
 **grok → seedream** 이고, 그 사다리(`multiroll_gemini.gen_fn_ladder`)는
 **클라이언트**를 단계마다 갈아 끼우는 구조다. 그래서 그 슬롯에 들어갈
 얼굴이 필요하다 — `GrokImageClient` 가 같은 이유로 `GeminiImageClient` 를
 상속한 것과 같은 자리다.
 ★사다리의 `_cross_client()` 는 「primary 가 Grok 이 아니면 Grok」이라,
  이 클라이언트를 primary 로 두면 **교차가 grok** 이고 그다음이
  seedream 이다 — 지시한 순서가 그대로 나온다.
 ★단계는 **여섯**이지 셋이 아니다 (Codex 정정): GPT → GPT 같은 모델
  재시도 → Grok → Seedream → GPT 연화 → Grok 연화. 그리고 이 사다리
  자체가 `still_safety_fallback_enabled=True` 일 때만 쓰인다.

상속 재사용: `set_context`/`_ctx`/`_trace_step`/`_log_ctx`/`_opik_meta`.
오버라이드: 운반층(`generate_image`)뿐이다.

★기록은 **`call_gpt_image_bytes` 가 한다**(`record_provider_call`).
 여기서 `image_tracer` 로 또 적지 않는다 — 같은 호출을 두 번 세게 된다.
★예산 문은 여기서 지난다(`reserve_current_call`) — 다른 gpt-image 자리와
 같은 관례다.
    )annotationsN)Path)AnyDictListOptionalTuple)reserve_current_call)GeminiImageClient1536x864864x1536	1024x1024)z16:9z9:16z1:1zDict[str, str]SIZE_BY_ASPECTc                    | st         S t        j                  t        |       j	                               }|r|S t
        j                  d| t                t         S )u!  비율 문자열 → 크기. 모르는 값은 **기본(16:9)** 으로 두고 경고한다.

    ★크게 실패시키지 않는 이유: 이 경로는 16:9 만 받는다. 뜻밖의 값이
     오면 그림을 잃는 것보다 **16:9 로 그리고 로그에 남기는** 쪽이 낫다.
    uA   gpt_image_client: 모르는 비율 %r — 기본 %s 로 그린다)DEFAULT_SIZEr   getstrstriploggerwarning)aspect_ratiohits     V/Users/manta/Documents/Projects/TheRoad-I1/backend/app/modules/llm/gpt_image_client.pyresolve_sizer   ;   sO     


S.446
7C


NNKl$     c                  `     e Zd ZdZ	 	 	 d	 	 	 	 	 d fdZddZ	 	 	 d	 	 	 	 	 	 	 	 	 d	dZ xZS )
GptImageClientuJ   gpt-image-2.5 — `generate_image(prompt, labeled_references=...)` 계약.c                n    ddl m} t        |   ||xs t	        |dd             || _        d | _        y )Nr   )settingsopenai_image_modelzgpt-image-2.5-sunburst)api_keymodel)app.core.configr   super__init__getattr_quality_openai_client)selfr!   r"   quality_s	__class__s        r   r%   zGptImageClient.__init__O   sH     	3 D7(*BD 	 	

  #'r   c                V    | j                   ddlm}  |       | _         | j                   S )Nr   )_resolve_openai_client)r(   $app.core.steps.shot_conti_light_stepr.   )r)   r.   s     r   _clientzGptImageClient._client\   s,    & #9":D"""r   c                J   ddl m} t        |xs g       }|s|r|D cg c]  }d|f }}t        j                         }t        j                  d      5 }	g }
t        |      D ]  \  }\  }}|t        d|d      t        |t        t        f      rGt        |	      |d	d
z  }|j                  t        |             |
j                  t        |             wt        t        |            }|j!                         st        d|d|       |
j                  t        |              t#        d        || j%                         |
rdnd||
xs d| j&                  t)        |      | j*                  ddt        | j,                  j/                  d      xs d      | j,                  j1                         D ci c]  \  }}|dvr|| c}}      }ddd       st3        d      |t5        t        j                         |z
  dz        fS c c}w c c}}w # 1 sw Y   FxY w)u  (PNG bytes, ms) 반환. 참조가 있으면 `edit`, 없으면 `generate`.

        ★**라벨은 그림에 안 실린다.** gpt-image 의 `images.edit` 은 파일을
         **순서대로** 받을 뿐 라벨 칸이 없다 — 무엇이 무엇인지는 **문안이**
         말한다. 그래서 여기서 라벨을 버리되 **순서는 지킨다**. 수동 수리
         8장이 같은 방식으로 나갔고 다 맞았다.
        ★참조를 **조용히 빼지 않는다** — 하나가 사라지면 아예 다른 계약의
         그림이 되는데 기록은 원래 계약으로 남는다(`gpt_image_gen` 주석의
         2026-08-03 실측).
        r   )call_gpt_image_bytes gptimg_)prefixNu   gpt_image_client: 참조 u    이 비었다02dz.pngu    파일이 없다 — zgpt_image_client.generate_image)sourceeditgenerate   )r"   sizer*   noperation_type)
project_id
episode_idr=   )modeprompt	ref_pathscall_kwargscapture_rolecapture_metadatau   gpt-image: 빈 응답i  )#app.modules.llm.gpt_image_primitiver2   listtime	monotonictempfileTemporaryDirectory	enumerateFileNotFoundError
isinstancebytes	bytearrayr   write_bytesappendr   existsr
   r0   _modelr   r'   _ctxr   itemsRuntimeErrorint)r)   rA   reference_imagesr   labeled_referencesr2   pairsbt0_tmprB   ilabelvalpkvpngs                     r   generate_imagezGptImageClient.generate_imagef   s   " 	M'+,>,D"'E)&67&6b!W&6E7^^((	:d#%I#,U#3<E3;+3E9NKM McE9#56 T
#wd^3AMM%*-$$SV,SNxxz+3E9<RSTRUVX X  Q( $4$ !(IJ&(Vj#+t![[(6#}}	 !/?!@!FBG%)YY__%6"%6TQ NN qD%6"C+ ;L  677C)B.$6777c 8H"C ;:s$   HE%H:H	HHH")NNhigh)r!   Optional[str]r"   rh   r*   r   returnNone)ri   r   )NNN)
rA   r   rY   zOptional[List[bytes]]r   rh   rZ   zOptional[List[Tuple[str, Any]]]ri   zTuple[bytes, int])__name__
__module____qualname____doc__r%   r0   rf   __classcell__)r,   s   @r   r   r   L   sw    T04(, &(%((+/(# 37&*>BF8F8 0F8 $	F8
 <F8 
F8r   r   )r   rh   ri   r   )rn   
__future__r   loggingrJ   rH   pathlibr   typingr   r   r   r   r	   app.core.image_call_budgetr
   #app.modules.llm.gemini_image_clientr   	getLoggerrk   r   r   __annotations__r   r   r    r   r   <module>ry      sm   8 #     3 3 ; A			8	$ " 
 "`8& `8r   