
    ޯj.                       d Z ddlm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 ddlmZ ddlmZ  ej"                  e      Zd	Zd(d
Zd)dZdZdZdZdZdZd*dZd)dZd+dZddd	 	 	 	 	 	 	 	 	 	 	 	 	 d,dZdZ dZ!d-dZ"d.dZ#dd	 	 	 	 	 	 	 	 	 d/dZ$d0d1dZ%d0d2dZ&	 	 	 	 	 	 	 	 d3dZ'd	Z(d0d4d Z)d!d"	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d5d#Z*	 	 	 	 	 	 	 	 	 	 d6d$Z+	 	 	 	 	 	 	 	 	 	 d7d%Z,dddd&	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d8d'Z-y)9u  i2i 시네마틱 변환 스테이지 (2026-08-13 #108) — sel 확정 후 grok 2.0 변환.

사용자 확정 구조(하이브리드): 최종 스틸 = nb2 생성·판정·수정(sel 확정)
→ grok 2.0 i2i "시네마틱 재구성" → 변환본이 scene ImageAsset primary 로
영속. 원본 `_sel.png` 은 그대로 보존되어 prev 체인 앵커(기하 SOT)로
남는다 — 변환은 최종 룩 패스일 뿐 체인 참조 재료가 아니다.

계약:
  · 문안 = still_recipe 팩 단일 스템 `cine_transform`
    (CINE_TRANSFORM_PROMPT_VERSION) — 짧은 범용 지시 하나, 원본 조립
    프롬프트·시나리오 고유명사 불사용. 화풍 교체 = 팩 버전 교체.
  · 지문 = 계약 버전 + 문안 스템 내용 해시 + 모델 + 원본 sel bytes —
    sel 재생성·문안 개정·모델 교체 어느 것이든 변환을 다시 이끈다.
  · 재사용 = 지문 일치 + applied + 산출 파일 실재 → 유료 호출 0,
    records 무변경 (#77-B `_jit_tag_snapshot` 지출 탐지가 거짓 지출을
    읽지 않는 전제 — 저장 record 를 그대로 돌려준다).
  · 실패 = 원본 fallback + 실패 기록(records) — 샷은 원본으로 영속돼
    체인(prev)을 막지 않는다. ★재시도는 JIT 가 아니라 **스텝 미봉인**이
    이끈다(Codex R1 BLOCK-1): 완료+config 일치 스텝은 whole-step SKIP
    이라 "다음 방문"이 오지 않는다 — 걷기 끝에 서비스가
    StillCineTransformIncomplete 를 raise 해 스텝을 완료로 닫지 않고,
    resume 재진입(전 샷 재사용 지출 0)이 실패 변환만 다시 산다.
    예외: ImageCallBudgetExceeded 는 삼키지 않는다 — 예산 브레이크를
    fallback 으로 눌러 버리면 "변환된 최종본" 오독의 조용한 미변환
    완주가 된다(전파 → 샷 실패 격리 → 스텝 실패 → 운영자 확인).
  · **검열 포기**(2026-08-19 사용자 결정) — 위 되풀이가 끝나지 않는
    경우가 실측으로 나왔다. 같은 원본이 xAI 검열에 나흘간 일곱 번 전부
    막혔고(S49sh10·S73sh6), 막는 것이 지시문이 아니라 **그림 내용**이라
    다시 보낼수록 같은 자리에서 막힌다. 게다가 **거부당해도 요금이
    나간다**(거부 응답에 `cost_in_usd_ticks` 가 붙는다). 그래서 같은
    지문에서 검열 거부가 `still_cine_moderation_give_up_after` 회 쌓이면
    그 변환을 포기로 기록하고(`declined`) 원본을 최종본으로 확정한다 —
    다음 방문은 유료 호출 없이 그 기록을 돌려주고, 서비스는 그 샷을
    미완으로 세지 않아 스텝이 닫힌다. 원본 sel 이 다시 만들어지면 지문이
    달라져 저절로 한 번 더 시도한다(포기는 이 입력에 대한 것이지 이
    샷에 대한 영구 선고가 아니다).
  · 쓰기 순서 = 산출 파일 durable(원자 쓰기) 후 records 기록 — 크래시
    창에서는 기록 없는 파일이 남고, 다음 방문이 재변환한다(재지출
    ~$0.03 1회 허용이 기록 없는 자산 신뢰보다 싸다).

경계(알려진 것):
  · prev 앵커 fallback(still_recipe_service — recipe `_sel.png` 부재 시
    DB primary 사용)은 변환 ON 완주본에서 변환본을 앵커로 집는다.
    변환은 장소·인물·순간 보존 계약이라 기하 권위는 유지되지만 룩이
    섞인다 — 정상 걷기(스토리 순서, sel 실재)에서는 닿지 않는 경로.
    )annotationsN)datetimetimezone)Path)AnyDictOptional)ImageCallBudgetExceeded)is_moderation_error   c                l    	 ddl m} t        | |      syt	        | dd      dk(  ryy# t        $ r Y yw xY w)ui  제공자가 **결말을 지었다**고 말한 실패인가 (재시도·다시 가져오기 불가).

    접수 기반 제공자에서만 뜻이 있다. 다시 가져오기할 산출이 아예 없다는 뜻이므로
    접수 표식을 걷어야 다음 방문이 새로 살 수 있다 — 안 걷으면 없는 작업을
    영원히 가져오려 든다.
    r   )ReveTerminalErrorF
error_type submit_record_failedT)!app.modules.llm.reve_image_clientr   	Exception
isinstancegetattr)excr   s     Y/Users/manta/Documents/Projects/TheRoad-I1/backend/app/modules/pipeline/cine_transform.py_is_terminal_provider_errorr   A   sH    G c,-
 sL"%)??  s   ' 	33c                 ~    ddl m}  t        | dt              }	 t	        |      S # t
        t        f$ r	 t        cY S w xY w)Nr   settings#still_cine_moderation_give_up_after)app.core.configr   r   %CINE_MODERATION_GIVE_UP_AFTER_DEFAULTint	TypeError
ValueError)r   raws     r   _give_up_afterr#   W   s@    (
7-/C53xz" 5445s   
$ <<cine_transformcine_stage_directionzSOURCE STILL   cine_v1c                 :    ddl m}  t        t        | dd            S )Nr   r   still_cine_verify_enabledF)r   r   boolr   r   s    r   
_verify_onr+   t   s    ("=uEFF    c                 @    ddl m}  ddlm}  |t	        | dd            S )uO  설정 회차 — **이제 판정을 안 바꾼다** (2026-08-27, #92 이중 모델).

    ★옛 뜻: 「같은 모델에게 몇 번 물을까」. 지금 계약은 「두 모델에게 각
     한 번씩」이고 `cine_verify.verify_models()` 가 언제나 짝 전체를
     돌려준다 — 이 값은 모델 수를 못 바꾼다.

    ★그런데 함수는 남겨 둔다: 호출부가 `rounds=` 로 넘기고 있고, 인자를
     지우면 「무엇을 무시하는지」가 코드에서 사라진다. 검사를 끄는 것은
     `still_cine_verify_enabled` 플래그 하나다.
    r   r   )normalize_roundsstill_cine_verify_roundsr   )r   r    app.modules.pipeline.cine_verifyr.   r   )r   r.   s     r   _verify_roundsr1   z   s     )AGH.H!LMMr,   c                    t        | t              syt               syddlm} | j                  d       |t                     k(  S )u~  저장된 판정이 **지금 계약의 것**인가.

    판정 문안·**모델 짝**·물리 모델명·잡는 축이 바뀌면 옛 판정은 다른
    질문의 답이다. 이 갈래는 그림을 다시 사지 않고 판정만 다시 산다 —
    그래서 계약 sha 를 변환 지문(`cine_fingerprint`)이 아니라 여기서만 본다.

    ★**회차는 이제 계약이 아니다** (2026-08-27). 짝 전체가 언제나 판정하고
     `verify_contract_sha` 도 회차 대신 짝 크기를 접는다 — 설정을 2→1 로
     바꿔도 다시 판정하지 않는다(나가는 물음이 한 글자도 안 바뀐다).
    Fr   )verify_contract_shacontract)r   dictr+   r0   r3   getr1   )prior_verifyr3   s     r   _verify_is_currentr8      sD     lD)<D J'+>,  r,   )project_configcontextc                 
 t               syddlm} t        |xs i       } |||t	               |dD ci c]  }||v r|||    c}      

| d<   
j                  d      ry
j                  d      xs g }	d	| d
<   d| d<   ddj                  |	      z   | d<   t        j                  d|dj                  |	      dj                  
fd|	D              dd        yc c}w )u  산출을 재고 기각 여부를 rec 에 접는다.

    2026-08-26 계약 교체 — **변환본이 사진으로 성립하는가**만 본다.
    손가락 여섯 개·공중에 뜬 사람·문 여덟 개짜리 차 같은 파탄만 잡고,
    성립하면 **무조건** 변환본을 최종본으로 쓴다. 빛이 바뀌고 카메라가
    옮겨 간 것은 이 단계가 시켜서 하는 일이라 기각 근거가 아니다.
    자세한 근거는 `cine_verify` 머리말.

    ★기각은 **실패가 아니라 확정된 결말**이다 — `applied=False` +
     `rejected=True` 로 두어 호출측이 원본을 최종본으로 확정하고 스텝을
     닫게 한다. 실패로 세면 스텝이 영영 안 닫혀 재개마다 앞 단계가 다시
     돌고 연쇄 재생성이 난다(검열 포기가 같은 이유로 이 모양이다).

    ★판정 자체가 못 돌았으면(판정기 오류) **기각하지 않는다** — 이미 산
     그림을 판정기 사정으로 버리면 돈만 나가고 결과가 나빠진다.
    Nr   )verify_cine_result)
project_id
episode_idstill_idscene_index
shot_index)
result_png
source_pngroundsr9   opik_metadataverifyokbrokenFappliedTrejectedzimplausible:,rejected_reasonuv   cine_transform %s: 변환본이 사진으로 성립하지 않는다(%s) — 원본을 최종본으로 확정한다. %sz, z; c              3     K   | ]>  }| d j                  d      xs i j                  |i       j                  dd        @ yw)=findingsnoter   N)r6   ).0cvs     r   	<genexpr>z _apply_verify.<locals>.<genexpr>   sM      #!a Cq!%%
+1r66q"=AA&"MNO!s   AAi,  )	r+   r0   r<   r5   r1   r6   joinloggerwarning)rectagrC   rB   r9   r:   r<   metakrH   rS   s             @r   _apply_verifyr\      s   * <C2D*5C5 '(89T	 $q'z5C	A CMuuT{UU8_"FC	NC
O+chhv.>>C
NN	+,/61B		 #!# 	##'C	)*Cs   Cz::cine_humankeep_originalc                    |  t          S )N)CINE_HUMAN_SUFFIX)rY   s    r   cine_human_keyr`      s    U$%&&r,   c                    | j                   j                  t        |            }t        |t              r|j                  d      t
        k(  r|S y)uN   사람이 이 샷의 변환본을 거절했나 — 그 기록, 없으면 None.decisionN)datar6   r`   r   r5   CINE_HUMAN_KEEP_ORIGINAL)recordsrY   rX   s      r   human_keep_originalrf      s>    
,,

>#.
/C#t!48P!P
r,   )rejected_cinec          
        t         |t        j                  t        j                        j                         dD ci c]  }||xs i j                  |       c}d}|| j                  t        |      <   | j                          |S c c}w )u  사람이 변환본을 거절했다 — 이 샷은 **변환 전 원본**을 최종본으로 쓴다.

    실측(컨트리로드 2판, 사용자 육안): 변환이 로봇 얼굴을 사람 얼굴로,
    파란 홀로그램을 불투명한 실물로 바꾸고, 사람을 둘로 늘리고, 팔을 셋으로
    만들었다 — 변환 전 원본은 다섯 샷 모두 멀쩡했다.

    ★`::cine` 기록에 rejected 를 손으로 심지 않는다. 그 기각은 검사가 꺼져
     있거나 판정 계약이 바뀌면 **복권된다**(`resolve_or_run_cine_transform` 의
     rejected 갈래) — 판정기의 기각과 사람의 거절은 다른 것이라 섞지 않는다.
    ★**샷 단위**다. 샷을 다시 만들어 원본이 바뀌어도 유지된다 — 사람이 본 것은
     「이 샷에서 변환이 망가뜨린다」이고, 새 변환이 괜찮다는 보장이 없다.
     되돌리려면 이 키를 지운다.
    )filestaged_sha256source_sha256fingerprintprovidermodel)rb   reasonatrg   )
rd   r   nowr   utc	isoformatr6   rc   r`   save)re   rY   ro   rg   r[   rX   s         r   record_human_keep_originalru      s    $ -ll8<<(224:;: #((++:;	C ),GLL$%LLNJ;s   B
r   c                $    |  d|rd| z   S dz   S )u  변환 기록의 **키**. 슬롯이 있으면 뒤에 붙인다.

    ★한 자리에 둔다 — 이 키는 `records` 에 쓰는 곳과 **발송 맥락을 씌우는
     소유자 자리** 두 곳이 쓴다. 두 번 적으면 한쪽만 고쳐져 「본체 생성」과
     「뒤의 변환」이 같은 이름으로 섞인다 (2026-09-20 Codex BLOCK 2).
    ★장부 쪽에서 규칙을 **다시 짐작하지 않는다** — 여기서 받아 쓴다.
    z::cinez::r    )rY   slots     r   cine_record_keyry     s%     U&>Dr$[99b99r,   c                *    |rd| nd}| | d| dz  S )u  변환본 관례 경로 — `_sel.png` 과 나란한 `{tag}_cine.png`.

    ★``slot`` — 제공자 자리. 빈 문자열이면 **주 제공자**이고 종전 경로 그대로다
     (byte-identical). 검열 대체처럼 **다른 제공자**가 같은 샷을 변환할 때만
     `{tag}_cine_{slot}.png` 로 갈라 둔다.

    ★[2026-09-09 Codex BLOCK 1] 안 가르면 대체 성공이 주 제공자의 거절 기록을
     **덮는다.** 그러면 같은 샷을 다시 방문할 때 주부터 또 호출하고, 대체 지문은
     주와 안 맞아 재사용도 못 해 **둘 다 다시 산다.** 제공자별 거절 누적
     (`moderation_refusals`)도 사라져 「둘 다 거절 후 확정」이 안 쌓인다.
    _r   _cinez.pngrw   )
recipe_dirrY   rx   suffixs       r   cine_output_pathr     s+      qZRF3%uVHD111r,   c           	         t        j                  |      j                         }t        j                  t         d|  d| d| j	                  d            j                         S )u  변환 1건의 재사용 지문 v1 — 문안(exact-stem)·모델·원본 bytes.

    ★옛 판이다. provider·endpoint 칸이 없어 **모델 문자열만** 적었는데,
     그 시절 변환은 전부 Grok 이었으므로 v1 record 는 「그 Grok 산출」을
     뜻한다. 지우지 않는 이유는 완주본을 다시 사지 않기 위해서다
     (dual-read — `_fingerprint_matches` 참조).
    |utf-8)hashlibsha256	hexdigestCINE_CONTRACT_VERSIONencode)stem_content_hashrn   	sel_bytessel_shas       r   cine_fingerprintr     s[     nnY'113G>> 
!#4"5QugQwiH	 ikr,   c                    ddl m}  |       }|y	  |        y# t        $ r t        j	                  d| xs d        w xY w)u  지금 이 결과를 **확정해도 되는지** 확인한다.

    스텝 경계에서 걸어 둔 손잡이를 그대로 부른다. 표가 안 걸린 직접 호출
    에서는 아무 일도 안 한다(no-op).

    ★부르는 자리가 둘이다 (2026-08-26 Codex 2차 재리뷰 BLOCK-2).
     ⓐ 제공자 결과를 받은 직후 — 그림은 파일로 남기고 확정만 멈춘다.
     ⓑ **판정이 끝난 뒤 기록을 쓰기 직전** — 판정 VLM 이 도는 동안 주인이
       바뀔 수 있고, 그러면 옛 worker 가 뒤늦게 `applied`/`rejected` 를
       남의 주행 자리에 적는다. ⓐ 만으로는 그 창이 안 닫힌다.
    r   )get_current_stop_checkNu1   cine_transform: 중단 확인에 걸렸다 — %su   안전 지점)app.core.image_call_budgetr   r   rV   rW   )wherer   _gs      r   
_stop_gater   3  sK     B		!B	z
 J/	1s    %?T)
run_verifyc               @    |rt        | |||||       t        |       y)u  판정하고 → **아직 내가 주인인지 보고** 나온다. 기록은 호출부가 한다.

    ★두 동작을 한 함수로 묶는 이유 (2026-08-26 Codex 3차 재리뷰 BLOCK-1):
     판정을 부르는 자리가 다섯 곳인데 확인을 손으로 하나씩 붙였더니
     **두 곳을 빠뜨렸다**(이미 쓰던 그림을 새 계약으로 다시 보는 갈래,
     버렸던 그림을 되살려 다시 보는 갈래). 판정 VLM 은 오래 도는데 그동안
     주인이 바뀌면 옛 worker 가 남의 자리에 결과를 적는다.

     그래서 `_apply_verify` 를 직접 부르지 말고 **반드시 이 함수를 지나게**
     한다. 빠뜨릴 자리를 없애는 것이 기억에 기대는 것보다 낫다.
    )rY   rC   rB   r9   r:   N)r\   r   )rX   rY   rC   rB   r9   r:   r   r   s           r   _verify_and_gater   L  s(    , csz!+%3W	F ur,   c                   t        j                  |      j                         }t        j                  | xs dj                  d            j                         }dj	                  t
        dt         |||j                  dd      |j                  dd      |j                  dd      |f      }t        j                  |j                  d            j                         S )u  변환 1건의 재사용 지문 v2 — 문안·스템·제공자 신원·원본.

    v1 에 두 가지가 더해진다.

    ⓐ `provider|endpoint`. 같은 문안·같은 원본이라도 **다른 제공자가 만든
       그림은 다른 산출**이다. 신원을 안 넣으면 제공자를 바꿔도 옛 산출이
       「지금 계약의 것」으로 통한다.
    ⓑ **실제 나가는 문안**. 연출 재료가 들어오면서 문안이 **샷마다 달라졌기**
       때문이다 — 스템만 보면 카메라 지시가 바뀌어도 지문이 안 움직여, 다른
       지시로 만든 그림이 「같은 조건」으로 읽힌다.

    ★스템 해시는 **빼지 않는다**. 문안 sha 로 갈음하면 「팩 교체 = 전 샷
     재변환」이 호출부가 스템에서 문안을 만든다는 전제에 얹히게 된다. 그
     전제가 언젠가 깨지면 화풍을 바꿔도 옛 그림이 그대로 남는다 — v1 이
     막던 실패라 그대로 지킨다(둘을 같이 접는 값은 0 이다).

    ## 원본은 **바이트 그대로** 접는다 — 의도한 비용 정책이다

    `sel_bytes` 는 PNG 파일의 날바이트다. 그림이 눈에 똑같아 보여도
    메타데이터가 다르거나 다시 인코딩했으면 지문이 움직이고, 그러면 변환
    요금이 한 번 더 나간다 (2026-08-26 Codex 2차 재리뷰 지적).

    그래도 바이트로 접는 이유는, 「같은 그림인가」를 값싸고 **틀리지 않게**
    판정할 방법이 이것뿐이기 때문이다. 픽셀만 비교하면 원본이 실제로 조금
    달라진 경우를 같은 것으로 읽어 옛 변환본을 최종본으로 통과시킨다 —
    잘못 아끼는 쪽의 손해가 잘못 쓰는 쪽보다 크다.

    ★대신 상류가 **같은 그림을 쓸데없이 다시 쓰지 않는 것**이 전제다.
     원본 sel 이 내용은 같은데 바이트만 달라지는 일이 실제로 관찰되면,
     그것은 여기서 막을 일이 아니라 상류에서 고칠 일이다.
    r   r   r   fprm   endpointrn   )r   r   r   r   rU   r   CINE_FINGERPRINT_VERSIONr6   )promptr   identityr   r   
prompt_shabasiss          r   cine_fingerprint_v2r   i  s    F nnY'113G2 5 5g >?IIKJHH
%&'Z$Z$Wb!	 	E >>%,,w/0::<<r,   c                   t        | t              sy| j                  d      t        k(  r| j                  d      |k(  S ddlm}  ||      xr | j                  d      |k(  S )u   저장된 record 가 **지금 조건의 것**인가 — 두 판을 나란히 읽는다.

    provider 칸을 그냥 지문에 더하면 **완주 판의 변환이 전량 재실행**된다
    (v1 record 의 지문이 하나도 안 맞으므로). 그래서 판 번호를 보고 갈린다:

      v2 record   그대로 v2 지문과 댄다
      v1 record   지금 선택이 **정확히 옛 Grok** 일 때만 지금 것으로 읽는다

    ★legacy 갈래에서 records 를 **1비트도 쓰지 않는다** — 판 번호를 붙여
     주려고 기록을 건드리면 재사용 바퀴가 지출로 세어진다(#77-B).
    ★소급 동등 처리 금지: 제공자 교체는 산출 계약이 실제로 다르다.
     조금이라도 다르면 재생성이 맞다.
    Ffingerprint_versionrl   r   )is_legacy_identity)r   r5   r6   r   "app.modules.pipeline.cine_providerr   )priorfp_v2fp_v1r   r   s        r   _fingerprint_matchesr     s^      eT"yy&'+CCyy'500Ex( 2		-(E13r,   )r9   r   rx   c                4  ./0123 |j                         }|ddlm}  |       .n|.t        |||      0t	        ||.|      /dV./0fd}t        j                  |      j                         }t        | |      2t        || |      }j                  j                  2      }t        |t              r ||      r|j                  d      du r|j                         rt               rt!        |j                  d	            ri |d
diS |j#                         D ci c]  \  }}||
 }}}t%        || ||j                         |
|	d       |j                  2<   j'                          i |d
diS t        |t              r ||      r|j                  d      du r|j                         rt!        |j                  d	            r2t(        j+                  d| |j                  d      xs d       i |d
diS |j#                         D ci c]  \  }}|dvr|| }}}d|d<   t               st(        j+                  d|        t%        || ||j                         |
|	dt                      |j                  2<   j'                          i |d
diS t        |t              rK ||      rB|j                  d      du r.|j                  d      dur|j                         r	|j                         }t-        |j                  d      xs d      }|rt        j                  |      j                         |k(  r|j#                         D ci c]  \  }}|dvr|| }}}d|d<   d|d<   t%        || |||
|	dt                      |j                  2<   j'                          t(        j+                  d|        i |d
diS t(        j/                  d| |j                  d      xs d       t        |t              r9 ||      r1|j                  d      du rt(        j+                  d |        i |d
diS t        |t              rM ||      rE|j                  d!      du r2t(        j/                  d"| |j                  d#      xs d       i |d
diS t        |t              r|j                  d$      du r|j                  d      r ||      rt1        |d%      rt-        |j                  d      xs d      }|j#                         D ci c]  \  }}||
 }}}	 ddl} |j4                  dWi |	 |t6        |fgd&}|j9                  |j:                        j<                  }d'|v rBt-        |j                  d'      xs d      |d'<   t-        |j                  d(      xs d      |d(<    |j:                  |fi |\  }} dd/l%m&}"  |"||       tI        d0       |jG                  d$d       |jG                  dd       d|d<   |jN                  |d1<   t        j                  |      j                         |d<   tQ        |       |d2<   d|d<   |jG                  d+d       t%        || |||
|	d3       |j                  2<   j'                          t(        j+                  d4| |       i |d
diS t        |t              r|j                  d$      du s|j                  d!      du r2 d5}#j                  j                  |#      }$t        |$tR              sg }$|$jU                  tW        jX                  tZ        j\                        j_                         |j                  d6d      |j                  d7d      |j                  d!      du rd!nd$|j                  dd      |j                  d8d      |j                  d9d      |j                  d'd      |j                  d(d      |j                  d:d      |j                  d#d      xs |j                  d+d      d;       |$t`         d j                  |#<   j'                          t(        j/                  d<| |$d=   d>   |$d=   d   xs d|$d=   d7   xs |$d=   d6   xs d?       d}%t        |t              r@ ||      r8|j                  d@      }&t        |&tP              rt        |&tb              s|&dkD  r|&}%dtW        jX                  tZ        j\                        j_                         /td        .j                  d8d      .j                  d9d      |t-        |	xs i j                  dA      xs d      |||jN                  |dB1i 3d}'dX.123fdC}(t1        |dD      r|jg                  |(       	  |j4                  dWi |	 |ji                  |t6        |fgE      \  }} dd/l%m&}"  |"||       i 1d|jN                  t        j                  |      j                         tQ        |       dF})3j                  d      rd|)d$<   t-        3j                  d      xs d      |)d<   t-        3j                  d'      xs d      |)d'<   t-        3j                  d(      xs d      |)d(<   t-        3j                  d7      xs d      |)d7<   |)j                  2<   j'                          tI        dG       d1d<   |jN                  1d1<   t        j                  |      j                         1d<   tQ        |       1d2<   3j                  d      rt-        3j                  d      xs d      1d<   t-        3j                  d'      xs d      1d'<   t-        3j                  d(      xs d      1d(<   t-        3j                  d7      xs d      1d7<   t%        1| |||
|	dH       1j                  d      s|'s3j                  d      rd1d$<   t-        3j                  d      xs d      1d<   t-        3j                  d'      xs d      1d'<   t-        3j                  d(      xs d      1d(<   t-        3j                  d7      xs d      1d7<   1j#                         D ci c]  \  }}||
 c}}j                  2<   j'                          i 1d
diS c c}}w c c}}w c c}}w c c}}w # t>        $ r}!tA        |!      jB                   d)|! dd* |d+<   tE        |!      r?|jG                  d$d       |jG                  dd       t(        j/                  d,| |d+          nt(        j/                  d-| ||d+          tI        d.       |j                  2<   j'                          i |d
dicY d}!~!S d}!~!ww xY w# tj        $ r  t>        $ rY}!ddIl6m7}*  |*|!      r,t(        j/                  dJ| tq        |!dKt-        |!                    tA        |!      jB                   d)|! dd* 1d+<   ddLl9m:}+ t        |!|+      rRd1d!<   t-        tq        |!dMd      xs tA        |!      jB                        ddN 1d#<   t(        jw                  dO| 1d+          tE        |!      }'ty        |!      rY|%dPz   },|,1d@<   t{               }-|-dkD  r'|,|-k\  r"d1d<   dQ1dR<   t(        j/                  dS| |,       n:t(        j/                  dT| |,|-       n!|%r|%1d@<   t(        j/                  dU| 1d+          Y d}!~!Bd}!~!ww xY wc c}}w )Yu  재사용 우선 변환 — 반환 record 로 최종 영속 원본이 갈린다.

    ★``slot`` — 제공자 자리(기본 "" = 주 제공자, 종전과 byte-identical).
     검열 대체처럼 다른 제공자가 **같은 샷**을 변환할 때 이것을 주면 저장 키와
     파일이 갈려, 주의 거절 기록과 대체의 결과가 **각각 보존**된다.

    반환: records 저장본과 동일 내용 + transient `reused`(스냅샷 제외
    키 — _jit_tag_snapshot 이 걸러낸다). applied=True 면 호출자가
    `file` 을 최종 영속 원본으로 쓰고, False 면 원본 sel 로 fallback.

    `identity` = 제공자 신원(provider·endpoint·model). 생략하면 지금 설정
    에서 푼다. `model` 은 **옛 지문(v1) 계산과 record 표시용**이라 호출부는
    신원과 같은 모델 문자열을 넘겨야 한다 — 어긋나면 legacy 갈래가 엉뚱한
    지문을 대게 된다.
    Nr   )cine_provider_identity)r   rn   r   )r   r   r   r   c                "    t        |       S )N)r   r   r   )r   )p_identr   r   s    r   _is_currentz2resolve_or_run_cine_transform.<locals>._is_current  s    #Ruv7 	7r,   rI   TrF   reusedu(   다시 본 판정을 확정하기 직전)rY   rC   rB   r9   r:   r   rJ   ug   cine_transform %s: 사진으로 성립하지 않아 버린 변환 — 원본 유지, 호출 없음 (%s)rL   r   )rJ   rL   rF   u   cine_transform %s: 검사가 꺼져 있어 버렸던 것을 되살린다 — 이미 요금이 나간 변환본을 최종본으로 쓴다 (호출 없음)u'   되살린 변환을 확정하기 직전)rY   rC   rB   r9   r:   r   r   stagedrj   )r   pending	recoveredu(   남겨 둔 변환을 확정하기 직전uv   cine_transform %s: 확정을 못 하고 끝난 변환을 되찾았다 — 다시 요청하지 않았다(요금 없음)Fuz   cine_transform %s: 남겨 둔 변환 파일이 기록의 해시와 다르다 — 확정하지 않는다 (접수 번호=%s)
request_idu   없음declineduN   cine_transform %s: 검열로 포기한 변환 — 원본 유지, 호출 없음submission_unknownu   cine_transform %s: 접수 여부를 모르는 변환이 남아 있다 — 자동으로 다시 보내지 않는다(요금 이중 방지). 사람이 확인해야 한다 (%s)submission_unknown_reasonr   fetch_submitted)r   labeled_references
status_urlresponse_urlz: i  errorub   cine_transform %s: 접수한 변환이 결과 없이 끝났다 — 접수 표식을 걷는다 (%s)uz   cine_transform %s: 접수한 변환(%s) 가져오기 실패 — 원본 fallback, 다음 방문에 다시 가져온다 (%s)u$   가져오기 실패를 적기 직전)atomic_write_bytesu$   접수한 변환을 되찾은 직후ri   
latency_msu'   되찾은 변환을 확정하기 직전uZ   cine_transform %s: 접수한 변환(%s)을 다시 가져왔다 — 다시 사지 않았다z::unresolved_logattempted_atsubmitted_atrm   r   rl   )archived_atr   r   ro   r   rm   r   r   r   rl   detailu   cine_transform %s: 결말을 못 본 옛 요청(%s · 번호=%s · 시도=%s)을 따로 남기고 새로 요청한다 — fal 대시보드에서 요금 여부를 대조할 것ro   ?moderation_refusalsmultiroll_tag)rI   r   rl   r   rm   r   rn   r   rx   packsource_filerk   c                   | j                  dt        j                  t        j                        j                                j                  |        i d| j                  dd      | j                  dd      xs j                  dd      | j                  dd      xs j                  dd      | j                  dd      | j                  dd      | j                  dd      d	j                  <   j                          y
)u  접수 즉시 신원을 durable 하게 — **산출 파일보다 먼저**.

        동기 왕복($0.03)에서는 「파일 durable 후 기록」이 맞았다: 크래시
        창에서 기록 없는 파일이 남고 다음 방문이 재변환하는데, 그 1회
        재지출이 기록 없는 자산을 믿는 것보다 쌌다. 접수 기반 $0.25 에는
        반대다 — **접수된 작업의 신원을 먼저 남겨야** 그것을 다시 가져온다.
        r   Tr   rm   r   r   r   r   )r   r   rm   r   r   r   r   N)

setdefaultr   rq   r   rr   rs   updater6   rc   rt   )infor   rX   rec_keyre   submitted_infos    r   _note_submitz3resolve_or_run_cine_transform.<locals>._note_submit  s     	 X\\2<<>	@d#!
!
 HH^R8R0NFJJz24NR0NFJJz24N((<4 ((<4 HH^R8!
W 	r,   set_submit_hook)r   )r   ri   rj   r   u   변환 결과를 받은 직후u!   새 변환을 확정하기 직전)is_abortu   cine_transform %s 중단 — %smessage)ImageSubmissionUnknowncause   uT  cine_transform %s: 접수 여부 불명 — 자동 재요청을 멈춘다(요금 이중 방지). fal 대시보드에서 접수 여부를 확인하고, 안 됐으면 records 의 submission_unknown 을 지운 뒤 다시 걷는다. 됐으면 그 번호를 request_id + pending 으로 넣으면 추가 요금 없이 결과만 가져온다. (%s)   
moderationdeclined_reasonu   cine_transform %s: 검열 거부 %d회 — 이 원본의 변환을 포기하고 원본을 최종본으로 확정한다 (거부에도 요금이 나가므로 다시 보내지 않는다)uQ   cine_transform %s: 검열 거부 %d회 (포기 기준 %d) — 원본 sel fallbacku=   cine_transform %s: 변환 실패 — 원본 sel fallback (%s))r   r   returnr*   rw   )r   Dict[str, Any]r   None)>
read_bytesr   r   r   r   r   r   r   ry   r   rc   r6   r   r5   existsr+   r8   itemsr   rt   rV   r   strrW   hasattrinspectset_contextCINE_SOURCE_LABEL	signaturer   
parametersr   type__name__r   popr   %app.modules.pipeline.multiroll_geminir   namer   listappendr   rq   r   rr   rs   _UNRESOLVED_LOG_LIMITr*   r   r   generate_imager
   app.core.run_controlr   r    app.modules.llm.image_send_stater   r   r   r#   )4rY   sel_pathr}   re   clientr   rn   r   r   r:   r9   r   rx   r   r   r   
source_shaout_pathr   r[   rS   rec_vrec_rstaged_bytes
staged_sharec_sridrec_pr   	_fetch_kw_paramspng
elapsed_msr   r   _orphan_key_orphansprior_refusals_pr_terminal_failr   
rec_stagedr   r   refusalslimitr   r   r   rX   r   r   s4      `                                          @@@@@@r   resolve_or_run_cine_transformr    s   > ##%IM!7!9+5E 
):9
.B7 	*446Jc4(G
C6HLLW%E5$IIi D(OO|1%))H2EF -e,Xt,, #(++-0-$!QA-0CI$,$7$7$9(6I	K !&W(%(4((5$IIj!T)OO eii12KK4UYY017R9 -e,Xt,, #(++- H-$!QFF A- Hi|KK^_bd 	CI$,$7$7$9(6H$.L		2
 !&W(%(4((5$IIh4'IIi ,OO  **,?39r:
'..6@@BjP ',kkm 8mda!66 TmE 8#E)!%E+U	(4,:G#M(2	6
 %*GLL!LLNKK?@CE .e-Xu--
 	8<(4H	6
 	5$IIj!T) 	\	 )%(4((5$II*+t3 	c67=2	? )%(4(( 	5$IIi D(IIl#F-.%))L)/R0"'++-0-$!QA-0'	.F))
 !(99'E&F)I ''(>(>?JJGw&*-eii.E.K*L	,',/IIn-3-5	.)4f44SFIFOC0 	M8S) 	9:		)T"		(D!i f!(!4!>!>!@o!*ol!k		'4 CI#(6H	J !&Wc	# *%)5)) %		)$99)*d2 !	!12<<##K0(D)H $<<5??A!IInb9!IInb9))$89TA ,%))L"5		*b1		*b1))L"5!IInb9 99]B7ii ;R@ 07B/!
 	$ %-.C-C-D$E[!m"h'RL&2(RL(OHRL,HOC	Q N%;u#5ii-.c3
3(=#' N  !X\\2<<>7 JJz2.JJz2. gm00AGRH  }}#-C4 &(NN 6 v()|,N%W% //!2I >? 0 
Z 	M8S)$

MM$^^C0::<j/

 l+$(Jy!'*""<06B(8J|$'*""<06B(8J|$),"">28b*:J~&
 *-"">28b*:J~& *W 	34ImmF&~~c2<<>O
OLl+ !$N$6$6|$D$J KC #N$6$6|$D$J KC"%"">28b#:C"%"">28b#:C#)(6B	DR GGI"""<0I 2 2< @ FBGL
   2 2< @ FBGL!."4"4^"D"JKN!."4"4^"D"JKN.1iik:kdaQTk:GLLLLN#c#8U##i 16HD8| 1$  	. $S	 2 232cU;DSAE'N*3/ 		)T*		,-67:E'NL JeGn. =>$)GLL!LLN-e-Xu---	.f #  ? 	2 C=NN<c"3	3s8<>s),,-Ru5ds;G 	Lc12(,C$%/2Wb)?T#Y-?-?0AAE#0GC+,LLD FI#g,X 5S9 s#%)H)1C%&"$EqyX."&J)5%&? 	 *5 -;)*NNOS\y?` ;sX    s sss-B#s H0v% 
|	v"!B6vv"v"%|8E||)r   BaseExceptionr   r*   )r   r   )r   r*   )r7   r   r   r*   )rX   r   rY   r   rC   bytesrB   r  r9   Optional[Dict[str, Any]]r:   r  r   r   )rY   r   r   r   )re   r   rY   r   r   r  )
re   r   rY   r   ro   r   rg   r  r   r   )r   )rY   r   rx   r   r   r   )r}   r   rY   r   rx   r   r   r   )r   r   rn   r   r   r  r   r   )r   r   r   r   )rX   r   rY   r   rC   r  rB   r  r9   r   r:   r   r   r   r   r*   r   r   )
r   r   r   r   r   Dict[str, str]r   r  r   r   )
r   r   r   r   r   r   r   r  r   r*   )rY   r   r   r   r}   r   re   r   r   r   r   r   rn   r   r   r   r   r   r:   r   r9   r  r   zOptional[Dict[str, str]]rx   r   r   r   ).__doc__
__future__r   r   loggingr   r   pathlibr   typingr   r   r	   r   r
    app.modules.llm.image_moderationr   	getLoggerr   rV   r   r   r#   CINE_TRANSFORM_STEMCINE_STAGE_DIRECTION_STEMr   r   r   r+   r1   r8   r\   r_   rd   r`   rf   ru   ry   r   r   r   r   r   r   r   r  rw   r,   r   <module>r     s  -\ #   '  & & > @			8	$ )* %,	5 '  3 "   ! GN"2 04(,,*	,*!$,*27,*EJ,*,,* &,* 
	,*b # * ' /3'*+ @:2 &)6;$  D 	 
 	
      
:/=/='*/=6D/=/= 	/=d333&)35C3	3L 04)-W$	W$ W$ 	W$
 W$ W$ W$ W$ W$ W$ W$ -W$ 'W$ W$ W$r,   