
    j#                   j   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mZmZ  ej                   e      ZdZdZdZd	Zd
ZdZdZdZdZdZdZdZdZdzdZ d{dZ!dZ"dZ#dZ$dZ%dZ&dZ'd|dZ(d}dZ)	 	 d~	 	 	 	 	 ddZ*	 	 	 	 	 	 	 	 ddZ+	 	 	 	 	 	 dd Z,	 	 dd!Z-dd"Z.dd#Z/d$d%d&d'd(d)Z0i d*d+d,d-d.d/d0d1d2d3d4d5d6d7d8d9d:d;d<d=d>d?d@dAdBdCdDdEdFdGdHdIZ1dHZ2dFZ3d.Z4d<Z5d2Z6dJZ7d8Z8d<Z9d@Z:dBZ;e;f	 	 	 ddKZ<ddLZ=e4f	 	 	 ddMZ>e2fddNZ?ddOZ@e2fddPZAddQZBdRZCdSeDdT<   ddUZEddVZFdWe2f	 	 	 	 	 	 	 ddXZGdYd6iZHdZZId*d[iZJd*ZKd\ZLeKf	 	 	 dd]ZMdd^ZNdd_ZOdd`ZPdaZQ	 	 	 	 ddbZRdcZSeSf	 	 	 	 	 	 	 dddZTddeZUddfdgddddh	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddiZVddddjdk	 	 	 	 	 	 	 	 	 	 	 	 	 ddlZWddmddn	 	 	 	 	 	 	 ddoZXddpdddq	 	 	 	 	 	 	 	 	 	 	 	 	 ddrZYddpde8dds	 	 	 	 	 	 	 	 	 	 	 	 	 ddtZZdddpe9ddu	 	 	 	 	 	 	 	 	 	 	 	 	 ddvZ[dddpe:ddu	 	 	 	 	 	 	 	 	 	 	 	 	 ddwZ\ddxde6dds	 	 	 	 	 	 	 	 	 	 	 	 	 ddyZ]y)uL  multiroll_gemini — multiroll_select 의 production 어댑터 (2026-07-13).

nb2(GeminiImageClient) 생성 + Gemini 단독 VLM 판정/결함 검사(call_structured
멀티모달 parts) 어댑터를 still_recipe_service 와 plate_multiroll 이 공용한다.
판정·결함 계약 텍스트는 prompts/_base/multiroll_judge 팩(SOT).
    )annotationsN)Path)AnyCallableDictListOptionalSequenceTuplemultiroll_judgez
gemini-prozclaude-opuszgpt-highg      ?g333333?zqwen-vlmg?gq_select_v1_margin020zopenrouter::qk_select_v3_qwen_main_gemini_assist_no_fallback_margin0209select_v3_equal_always_combine_binary_penalty_no_fallback2cross_model_order_v2_slot0_fwd_slot1_rev_parallel2c                    t        d | j                  d      xs g D        d      }t        |      dk  ry|d   xs d}|d   |d   z
  |z  S )	u]   판정 결과의 1위·2위 정규화 점수 차. 후보가 하나면 1.0(=둘째 불필요).c              3  F   K   | ]  }|j                  d       xs d  ywscorer   Nget.0vs     [/Users/manta/Documents/Projects/TheRoad-I1/backend/app/modules/pipeline/multiroll_gemini.py	<genexpr>z%_normalized_margin.<locals>.<genexpr>   s#     D)B1!%%.
A
)B   !verdictsT)reverse   g      ?r      )sortedr   len)resscorestops      r   _normalized_marginr&      s]    D)<)B)BDF 6{Q
).qC1Iq	!S((    c                    ddl m}  d| j                  fd| j                  fdt	        | dd      ffD cg c]	  \  }}|r| }}}t        |      dkD  r#ddlm}  |d	d
j                  |       dd      | j                  rqddlm} ddl	m
}  |       s |ddd      | j                  xs dj                         r| j                  xs dj                         s |ddd      t        t        gS | j                  r*ddlm} ddl	m
}  |       s |ddd      t        t        gS t	        | dd      rqddlm} ddlm}  |       s |ddd      | j                  xs dj                         r| j$                  xs dj                         s |ddd      t        t&        gS | j(                  st        gS | j*                  st,        gS t,        t&        gS c c}}w )u  후보 선정 판정 모델 목록 — 키가 있으면 이중, 없으면 기존 단독.

    G+Q ON 이면 [Gemini, Qwen] — 첫째가 우선 심판이다(`_judge_gq` 계약).
    ON+DASHSCOPE 키 없음 = fail-closed: 조용히 Gemini 단독으로 강등하면
    "이중으로 판정했다"는 기록·지문이 거짓이 된다. 지문 계산도 이 함수를
    지나므로 잘못된 구성은 지문 단계에서 이미 막힌다.
    r   settingsmultiroll_qk_judge_enabledmultiroll_gq_judge_enabledmultiroll_gg46_judge_enabledFr    AppErrorzmultiroll.judge_flags_conflictu    와 ud    가 동시에 ON 이다 — 판정 체계는 하나만. 한쪽 플래그를 내려라 (fail-closed)  codemessagestatus_code)qwen_configuredzmultiroll.qk_judge_unconfiguredu   multiroll_qk_judge_enabled=ON 인데 DASHSCOPE_API_KEY 가 비어 있다 — Qwen 메인 판정을 돌 수 없다. 키를 넣거나 플래그를 내려라 (fail-closed) z#multiroll.qk_judge_empty_model_slotu   qwen_vlm_model/gemini_text_model 중 빈 슬롯이 있다 — QK 이중 판정이 성립하지 않는다. 물리 모델 설정을 채워라 (fail-closed)zmultiroll.gq_judge_unconfiguredu   multiroll_gq_judge_enabled=ON 인데 DASHSCOPE_API_KEY 가 비어 있다 — Qwen 없이 G+Q 판정을 돌 수 없다. 키를 넣거나 플래그를 내려라 (fail-closed)has_openai_keyz#multiroll.select_judge_unconfiguredu   선정 판정 둘째 슬롯이 GPT 인데 OpenAI 키가 하나도 없다 — 키가 없으면 alias 가 Router 에 안 붙어 조용히 단독 판정이 된다. 키를 넣거나 플래그를 내려라 (fail-closed)z%multiroll.gg46_judge_empty_model_slotu   gemini_text_model/openai_model 중 빈 슬롯이 있다 — 선정 동등 이중 판정이 성립하지 않는다. 물리 모델 설정을 채워라 (fail-closed))app.core.configr*   r+   r,   getattrr"   app.core.errorsr/   joinapp.modules.llm.qwen_vlm_clientr5   qwen_vlm_modelstripgemini_text_modelQWEN_JUDGE_MODELJUDGE_MODELapp.core.openai_keysr8   openai_modelSELECT_JUDGE_MODEL_2anthropic_api_key#multiroll_dual_select_judge_enabledSELECT_JUDGE_MODEL)r*   nameon	_on_flagsr/   r5   r8   s          r   resolve_select_judge_modelsrL      s=    ) *002)002+X=uEG
	
r  	 
  	 9~,1<<	*+ ,I I 
 	
 ** 	-C 6;    ''-2446 228b??A:.    !+..**,C 6E    -..x7?& 	-7 :$
  	 	 **0b779 --3::<<8    122%%}77"## 455C	s   Gqk_reversedgqequal_crossopenrouter_prioritydual_marginsinglec                   t        |       }t        |      dk  rt        S |d   t        k(  rt        S t        |v rt
        S t        |      dk(  r|d   t        k(  r|d   t        k7  rt        S t        d |D              rt        S t        S )uT   선정 판정 갈래 — dispatch·owns_order·fallback 봉인의 **단일 출처**.r   r   r    c              3  F   K   | ]  }|j                  t                y wN)
startswithOPENROUTER_JUDGE_PREFIX)r   ms     r   r   z)select_judge_pair_kind.<locals>.<genexpr>T  s     
="Q1<</0"r   )listr"   PAIR_SINGLErA   PAIR_QK_REVERSEDPAIR_GQrB   PAIR_EQUAL_CROSSanyPAIR_OPENROUTER_PRIORITYPAIR_DUAL_MARGIN)modelsmss     r   select_judge_pair_kindrc   G  s    	fB
2w{	!u  2
2w!|1,A+1E  

="
==''r'   c                ,   | j                  d      xs g D ci c]'  }|j                  d      |j                  d      xs d) }}|rt        |j                               nd}|dk  ry|j                  |d      |j                  |d      z
  |z  S c c}w )uO  "Qwen 눈의 격차" — Qwen 정규화 점수에서 Q 승자 − G 승자.

    Gemini 우선 체계라 재는 것은 "Qwen 이 Gemini 승자에 얼마나 반대하는가"
    하나뿐이다. 전 후보 0점(top<=0)이면 격차를 잴 근거가 없다 — 0.0(=반대
    근거 없음)으로 떨어져 Gemini 채택이 된다.
    r   labelr   r           )r   maxvalues)q_resg_winnerq_winnerr   r$   r%   s         r   _q_gaprl   Y  s     		*-3363! eeGnquuW~223  6"(#fmmo
aC
axJJx#fjj1&==DD6s   ,BFc                   i }d}ddl m} ddlm}	 ddlm}
 ddlm} ddlm	} dfd} |	 |
 | ||                        } |t        |       	      5 }| D ci c]  }||j                  ||       }}ddd       | D ]  }	 |   j                         ||<    |st!        d      |t        |      dk(  r4t#        t%        |j'                                     \  }}i |dd| |gdiS | d   | d   }}||   ||   }}|j)                  d      |j)                  d      }}||||i}||k(  ri |ddd|t+        |      diS t-        |t/        |      t/        |            }|s)|t0        k  r i |d|t3        |d      |t+        |      diS t5        ||      }|r|j)                  d      xs i j)                  d      xs i }|j)                  d      xs g D cg c]>  }i |dt7        t3        |j)                  |j)                  d      d      dz              i@ c}|d<   dt3        |d      |t+        |      d|d<   |S c c}w # 1 sw Y   xY w# t        $ r$}|}t        j                  d
||       Y d}~d}~ww xY wc c}w )uU  우선+보조 동시 판정 — 2026-08-10 합의 규칙. margin-skip 없이 둘 다 부른다.

    슬롯 계약: models[0]=우선(취합) 심판, models[1]=보조(관찰·교차) 심판.
    · winner 일치 → 우선 판정 채택 (route=agree)
    · 불일치 & 격차<`GQ_DISAGREE_MARGIN` → 우선 채택 (route=priority_route)
    · 불일치 & 격차≥문턱 → `combine_select_verdicts` 합산 (route=combined)
    · 한쪽 실패 → 남은 쪽 채택+경고 (route=single_<모델>) / 전건 실패 → raise

    priority_route (2026-08-12 QK 전환): 우선 채택 route 의 기록 이름 —
    G+Q(기본)="gemini_priority" byte-identical, QK="qwen_priority".

    equal_disagree_combined (2026-08-13 G+G46): True 면 **동등 이중** —
    우선 채택 갈래를 걷어내고 불일치는 격차와 무관하게 전부 combined
    합산이다(동등 체계에는 격차 문턱이 정할 "우선 심판"이 없다). 이때
    models[0] 은 우선권이 아니라 agree 시 verdict shape 제공 슬롯이고,
    gap 은 기록으로만 남는다. False(default)=기존 수학 byte-identical.

    반환 shape 는 단독 판정 호환(winner/ranking/verdicts…) — 채택 경로와
    양쪽 승자·격차는 `gq` 키에 병기해 record·갤러리가 추적한다 (키 이름은
    체계 무관 공용 기록 슬롯이다).
    Nr   ThreadPoolExecutorbind_current_budgetbind_current_ledgerbind_current_tracebind_current_generation_contextc           	     >     | d| j                  dd             S N_-r6   replace)rX   onepartss    r   _callz_judge_gq.<locals>._call  s%    1eq3!3 4566r'   max_workersu8   G+Q 판정: %s 실패 — 남은 심판으로 진행: %ru   G+Q 선정 판정 전건 실패r    rN   single_)routera   winneragreerf   )r   gapper_model_winnerra      dualadjustedr   r   re     combined)rX   strreturnDict[str, Any])concurrent.futuresro   app.core.image_call_budgetrq   app.core.send_ledgerrs   app.modules.llm.opik_traceru   "app.services.image_capture.contextrw   r"   submitresult	ExceptionloggerwarningRuntimeErrornextiteritemsr   rY   rl   r   GQ_DISAGREE_MARGINroundcombine_select_verdictsint)ra   r~   r   labelspriority_routeequal_disagree_combinedoutlast_excro   rq   rs   ru   rw   r   _bound_poolrX   _futsexcr#   g_modelq_modelgqgwqwwinnersr   r   _adjr   s    ``                            r   	_judge_gqr   h  s   0 &(C(,H 6>8=7 !+,>u,EF	HIF 
F	45;<VELL++V< 
5	T1X__&CF  <=8K
3x1}d399;'(3E#Et}DEEay&)WGw<WqAUU8_aeeHoBGR(G	Rx2! 2TWS07&*3i1 2 	2 CGSW
%C"s-?'?2! 2T^E#qM07&*3i1 2 	2 'sF3H V$*//
;Ar ll:.4"4 
4 KqJ'3uTXXaeeGna%@4%GHIJ4 
  *%Q-*1T#YHHTNO[ = 
5	4
  	THNNJAsT T	TF 
s=   I
I7I
IAJI

I	J I??Jc                  $%&'() ddl m} ddlm} ddlm} ddlm}	 ddlm	}
m
} ddlm} | d   | d   c&)t        t                    }d$fd	$&d
D ci c]  }|| c}f)d| |
      fi(d%$(fd} | | | |	|                        } |d      5 }&)fD ci c]  }||j                  ||       }}ddd       i %i 'g }d}&)fD ]1  }(|   \  }}}	 |   j!                         }|'|<    |||      %|<   3 %st+        d      |d&%&'()fd}t,         |       &)gd}|r||d<   t/        %      dk(  rHt1        t3        %j5                                     \  }}(|   d   }|&)fv rd| nd}i |di |d|iiS %&   j7                  d      }%)   j7                  d      } || k(  |d<   &|)| i|d<   t9        %      }!|!j7                  d      xs i j7                  d      xs i }"|!j7                  d      xs g D #cg c]>  }#i |#dt;        t=        |"j7                  |#j7                  d       d      d!z              i@ c}#|!d<   || k(  r|!j7                  d      |k(  rd"nd#}i |d|i|!d<   |!S c c}w c c}w # 1 sw Y   xY w# t"        $ r6}|}|j%                  |       t&        j)                  d|||       Y d}~d}~ww xY wc c}#w )'u5  ★Gemini 정순 · 둘째 심판 역순 — **2콜 병렬** (2026-08-29).

    > "2, 6번은 Gemini 정순, Grok 역순 으로 병렬로 (즉 두번만)"

    ## 하는 일

        슬롯0(Gemini) ← 후보를 **정순**으로 제시
        슬롯1(둘째)   ← 후보를 **역순**으로 제시      ← 둘을 동시에
        역순 응답을 canonical 라벨로 **역매핑**
        두 canonical 결과를 GG46 규칙으로 결합

    ## 지키는 것

    역순 응답을 canonical 로 되돌린 **뒤에는** 두 모델 결과가 같은 후보 축에
    놓인다. 그래서 `combine_select_verdicts` 의 **모델별 최고점 정규화**와
    **하드위반 합집합**이 그대로 돈다 — 척도가 다른 두 모델을 원점수로 더하면
    큰 척도가 판정을 지배하고, 위반을 교집합으로 세면 「한쪽이 못 보는 축을
    다른 쪽이 채운다」는 이중의 목적이 사라진다.

    한쪽 실패는 생존 슬롯으로 진행(`single_forward` / `single_reverse`
    — 모델 이름은 route 에 안 박는다, `slots[].model` 이 SOT), 양쪽
    실패는 raise — 기존 계약 그대로다.

    ## ★잃는 것 — 숨기지 않는다

    모델과 순서가 **엮인다.** 「이 모델이 위치에 흔들리나」는 더 못 잰다.
    두 슬롯이 갈렸을 때 모델 차이인지 순서 차이인지도 못 가른다. 2콜
    계약의 본질적 대가이고 세 번째 콜을 몰래 넣어 보완하지 않는다.

    그래서 기록에 **`agreement` 라는 이름을 쓰지 않는다.** 옛 flip 의 그
    값은 「같은 모델이 순서에 안 흔들렸다」였다. 여기서는
    `slot_winner_match` — 「모델도 순서도 다른 두 슬롯의 승자가 같았다」일
    뿐이고, 그 뜻을 키 이름과 정책 문자열이 말한다.
    r   rn   rp   rr   rt   )flip_display_to_canonicalnormalize_flip_verdictrv   r    c                    ddg}|t              z  }t        t                    }t        |       D ]:  \  }}|j                  dd| dd       |j                  t	        ||                < |S )uG   제시 순서만 다른 같은 payload — 참조·지시문은 공유.texttyper   
Candidate :)	ref_partsdictzipappendpng_part)	orderr   by_labeldispsrc
cand_paths	head_textlabeled_refsr   s	        r   _partsz(_judge_cross_model_order.<locals>._parts  s{    .4i%H$Iy&&FJ/0VU+ID#JJ*TF!0DEFJJx./ , 
r'   forwardr   c           	     \    |    \  }}} |  |      d| j                  dd             S ry   r|   )modelrz   r   r   r~   slotss      r   r   z'_judge_cross_model_order.<locals>._call  s8    El5!5&-1U]]3-C,D)EFFr'   r   r   NuD   cross-model 판정: %s(%s) 실패 — 남은 슬롯으로 진행: %ru'   cross-model 선정 판정 전건 실패c            
         g } 	fD ]C  }|   \  }}}| j                  |||j                  |      j                  |      |v d       E | S )N)r   r   display_to_canonicalraw
normalizedok)r   r   )
r   r   
order_namer   d2ccanonr   r   r   x_models
        r   _slot_recordz._judge_cross_model_order.<locals>._slot_record<  sf    w'E%*5\"JsJJ#(+wwu~#ii.un  ( 
r'   )policyr   ra   failedr   single_unknowncross_model_orderr   r   slot_winner_matchslot_winnerr   r   r   r   re   r   cross_slot_agreecross_slot_combined)r   	List[str]r   List[Dict[str, Any]])r   r   r   r   )r   r   )r   ro   r   rq   r   rs   r   ru   %app.modules.pipeline.multiroll_selectr   r   r   rw   rY   reversedr   r   r   r   r   r   r    CROSS_MODEL_ORDER_POLICY_VERSIONr"   r   r   r   r   r   r   r   )*ra   r~   r   r   r   r   ro   rq   rs   ru   r   r   rw   
rev_labelslabr   r   poolrX   futsr   r   r   r   rz   r   r#   r   r   baser   r   xwr   r   r   r   r   r   r   r   r   s*    `````                              @@@@@@r   _judge_cross_model_orderr     s   L 6>8= ay&)GWhv&'J	 	 	)V&%A&3c3h&%AB)Z)B6)JKE
G !+,>u,EF	HIF 
	*d4;W3EF3Ea4;;vq))3EF 
+ (*ECF(,H7#"5\
As	(u+$$&CCJ 2#sFCE%L $ DE8S  3G$D
 X
5zQ$u{{}-.
s5\!_
 gw// :,'5E 	E#E*,Dt,DWe,DEE	w		H	%B	w		H	%B !#bD	"B4D 'uf5H LL &B++J7=2D ,,z*0b00A 	G1Fgs5!%%.!!<t!CDEF0HZ
 rhll84:  ' 
 %=t$<We$<H !OU &B G 
+	*   	(HMM% NNVz3( (	(HsC   !
J
&J-JJ*%J%AK'JJ"%	K$.+KK$c                   |D ci c]  }|d }}i }d}| j                         D ]  \  }}|j                  d      xs g }|j                  d      xs g }	|j                  d      r|dz  }|	rt        d |	D              nd}
|	D ]?  }|j                  d      }||v s||xx   |j                  d	      xs d|
xs dz  z  cc<   A |D ]Y  }|j                  d      }|j                  d
      xs g D ].  }||v s|j                  |g       j	                  d| d|        0 [  |D ci c]"  }|||   |j                  |      rt
        ndz
  $ c}t        |fd      }t        t        | j                                     }g }	|j                  d      xs g D ]h  }|j                  d      }|j                  |      xs g }|	j	                  i |d|j                  dd      |rddj                  |      z   ndz   i       j i }| j                         D ]X  \  }}|j                  d      xs g D ];  }|j                  t        |j                  d            g       j	                  |       = Z g }|D ]d  }|j                  |      xs g }t        |j                  |      xs g       }|s|s;|rt        |d         ni }|j	                  i |||d       f |d   ||	|t        |       xr |t        |       k(  t        |       |j                         D ci c]  \  }}|t!        |d       c}}j                         D ci c]  \  }}|t!        |d       c}}|| j                         D ci c]  \  }}||j                  d       c}}t        | j                         D ch c]  }|j                  d       c}      dk(  ddS c c}w c c}w c c}}w c c}}w c c}}w c c}w )u  이중 판정 합의 — 하드 위반 개수 페널티 + 모델별 정규화 점수 합.

    점수를 그냥 더하면 안 된다. 척도가 모델마다 다르다(0~100 대 0~10) — 원
    점수를 더하면 큰 척도 모델이 판정을 지배한다. 이 프로젝트에서 이미 겪은
    함정이라 모델별 최고점으로 정규화한 뒤 더한다.

    하드 위반은 **합집합**으로 센다. 한쪽이 못 보는 축을 다른 쪽이 채우는 것이
    이중의 목적이므로 교집합을 쓰면 그 목적이 사라진다.

    반환 shape 는 단독 판정과 호환된다(winner/ranking/verdicts) — 호출측
    코드와 기록 계약을 그대로 둔다.
    rf   r   readingsr   all_candidates_failr    c              3  F   K   | ]  }|j                  d       xs d  ywr   r   r   s     r   r   z*combine_select_verdicts.<locals>.<genexpr>  s!     :A155>&Q&r   re   r   hard_violations[z] c                    |     S rU    )r   adjs    r   <lambda>z)combine_select_verdicts.<locals>.<lambda>  s    c#hYr'   )key
verdict_kor6   u     ★위반: z / )re   r   r   r   )ra   r   r   
violationsr   agreed)r   rankingr   r   r   r   )r   r   rg   
setdefaultr   SELECT_VIOLATION_PENALTYr!   r   r   rh   r<   r   rY   r   boolr"   r   )	per_modelr   r   normvion_failr   r#   r   r   r%   r   rhvr   firstextrarows_by_label_m_resrowsbase_rowkrX   r   s                           @r   r   r     s    399&3c3h&D9 "CFoo'
s77:&,"77:&,"77()aKF>Fc:::AA%%.Cd{S	aeeGn1chQ??	  A%%.Cee-.4"4$;NN3+22QugRt3DE 5  (@ s S	 # 6257 7C V!67G i&&()*EHii
#)r)eeGn" 

155r2EJo

50AAPRT
 	 *2 68MOO%D((:&,",A$$Sw%8"=DDQG - & H  %+#''#,$"%B$(4Q=bI8IcbIJ  !*
  $IK6S^3K9o6:jjlClda1eAqk>lC47IIK@KDAqE!QKK@-6__->!@->TQ "#AEE(O!3->!@I4D4D4FG4Fq155?4FGHAM
 ] :Br D@!@Gs#   
O*'OOO%9O+/O1c           
        i }d}| D ]v  }	  |||d|j                  dd             ||<   t	        |      dk(  s3t	        |       dkD  sBt        ||         }|t        k\  sZt        j                  d||       ||   c S  |st        d      |t	        |      dk(  r"t        t        |j                                     S t        ||      S # t        $ r#}|}t        j                  d||       Y d}~d}~ww xY w)	u  조건부 이중 판정.

    첫 심판을 먼저 부르고, 그가 1위를 `SELECT_DUAL_MARGIN_SKIP` 이상으로
    앞세우면 둘째를 부르지 않는다 — 그 구간에서는 둘째가 판정을 거의
    뒤집지 않는다는 것이 208샷 실측이다. 근소하면 둘째를 불러 합의한다.

    한쪽이 죽으면 남은 쪽으로 진행한다. 둘 다 죽으면 원인을 달아 올린다.
    Nrz   r{   r6   u;   이중 판정: %s 실패 — 남은 심판으로 진행: %rr    uA   선정 판정: %s 가 1위를 %.2f 앞세워 둘째 심판 생략u"   이중 선정 판정 전건 실패)r}   r   r   r   r"   r&   SELECT_DUAL_MARGIN_SKIPinfor   r   r   rh   r   )	ra   r~   r   r   r   r   rX   r   margins	            r   _judge_dualr    s    &(C(,H	EQqyyb'9&:#;<CF s8q=S[1_'A/F00Wv 1v   ?@hN
3x1}D&''"3//%  	HNNMqRUW		s    C

	C6C11C6c                     ddl m}  | j                  rt        S | j                  rt
        S t        | dd      rt
        S | j                  rt        S t
        S )u	  후보 선정 판정에 쓸 model alias — 키가 없으면 기존 Gemini 로 남는다.

    지문(fingerprint)에도 이 함수의 반환값을 실어야 한다. 판정 모델이 바뀌면
    선정 결과가 바뀌므로 산출이 무효화돼야 한다.
    r   r)   r-   F)	r9   r*   r+   rA   r,   rB   r:   rF   rH   r)   s    r   resolve_select_judge_modelr    sN     )****x7? !)!;!;LLr'   c                     ddl m}  t        | j                  t        | j
                  t        | j                  t        | j                  idj                  fdt               D              S )u[  선정 판정의 **물리** 모델 문자열 — 지문 스탬프용.

    alias 는 그대로인 채 뒤의 실제 모델만 바뀌는 경우(판 교체)를 지문이 잡아야
    하므로 alias 와 따로 기록한다. 이중일 때는 두 모델을 모두 싣는다 — 한쪽만
    바뀌어도 어느 롤이 뽑히는지가 바뀐다.
    r   r)   +c              3  B   K   | ]  }j                  ||        y wrU   r   )r   rX   physs     r   r   z6resolve_select_judge_model_physical.<locals>.<genexpr><  s     J,IqDHHQN,Is   )r9   r*   rH   anthropic_judge_modelrE   rD   rB   r@   rA   r>   r<   rL   )r*   r  s    @r   #resolve_select_judge_model_physicalr  -  sX     ) 	H::h33X//(11	D 88J,G,IJJJr'   ONETWOTHREEFOURFIVE)r    r   r         1z1.2026071323002z2.2026071616103z3.2026072116004z4.2026072211305z5.2026072300006z6.2026080622007z7.2026080711008z8.2026081014009z9.20260812175010z10.20260813112111z11.20260813204512z12.20260814130513z13.20260819233214z14.20260827170015z15.20260827234016z16.202608280010gptc                    ddl m} t        |       } |t        d|      j	                          |t        d|      j	                         dS )u  참조 선별 ON 에서만 쓰는 '없는 것 새로 넣기' 절 (2026-08-19).

    수정 지시문은 기본적으로 "여기 적힌 것만 고치고 나머지는 그대로"인데,
    지적이 지금 사진에 **아예 없는** 인물·소품을 넣으라고 할 때가 있다
    (시험 실측 S55sh1 인물 1명·S48sh9 스탠드). 그때만 이 절이 끼고 그
    참조도 함께 붙는다 — 사용자 지시.
    r   load_promptfix_missing_headversionfix_missing_tail)missing_headmissing_tail)app.modules.prompt_loaderr4  resolve_judge_pack_versionJUDGE_MODULEr?   pack_versionr4  resolveds      r   load_fix_missing_textsrA    sK     6),7H#,h@@E#,h@@E	 r'   c                 l   ddl } ddl}ddlm}  |d      d   d   d   }d	D ci c]  }||d   |    }}| j	                  t
        j                  d
            }|j                  d       |j                  |j                  |dd      j                  d
             |j                         dd S c c}w )u#  참조 선별의 실질 지시문 지문 — 팩 밖 **코드 문자열**이라 따로 접는다.

    선별이 실제로 무엇을 붙일지 정하는 문안은 둘이다: 번호가 무엇을
    가리키는지 알려 주는 `_REF_INDEX_HEADER` 와, 결함 검사 스키마 안
    선별 세 칸의 설명("참조를 붙이는 것은 공짜가 아니다 … 애매하면 아무
    것도 적지 마라"). 이 둘은 팩이 아니라 코드에 있어 팩 내용 해시
    (`fix_missing_pack_content`)가 못 덮는다 — 문안을 고치면 붙는 참조와
    최종 그림이 달라지는데 모든 해시가 그대로여서, 반대 정책으로 만든
    산출이 "같은 조건"으로 기록된다(2026-08-08 스테일 재사용 사고와 같은
    부류).

    스키마에서 **되읽어** 해시한다 — 상수를 따로 두고 손으로 맞추면 한쪽만
    고쳐질 때 조용히 어긋난다. 대상은 선별 세 칸으로 한정하므로
    severity·observation_index 등 다른 칸의 문안 개정에는 안 움직인다.
    r   N)build_critique_schemaT)with_ref_gate
propertiesissuesr   )needs_ref_indicesadds_missing_entitymissing_entity_namezutf-8    F)	sort_keysensure_ascii   )
hashlibjsonr   rC  sha256_REF_INDEX_HEADERencodeupdatedumps	hexdigest)rN  _jsonrC  itemr  gatehs          r   fix_ref_contract_sharZ    s      K (**244;=D))A 	
4a  ) 	 
 	(//89AHHWHHU[[5  228&/C;;="s   B1c                Z    ddl m}  |t        dt        |             j	                         S )u   fix-rejudge 2후보 중립 판정 헤더 (Codex HIGH-4).

    원본/수정본 provenance 를 노출하지 않고 '타깃 계약 충실도'로만 판정
    — 헤더 문구=팩 v3 fix_rejudge_header 스템 SOT.
    r   r3  fix_rejudge_headerr6  )r;  r4  r=  r<  r?   )r?  r4  s     r   load_fix_rejudge_headerr]  :  s+     6**<8 egr'   c           	     n    	 t         |    S # t        $ r  t        d| dt        t                d      w xY w)Nz&unknown multiroll_judge pack selector 	 (known: ))JUDGE_PACK_VERSION_MAPKeyError
ValueErrorr!   selectors    r   r<  r<  J  sN    
%h// 
4XL A456a9
 	

s    )4c                (    ddl m}  |t        |       S )u   판정 팩 내용 해시 — #77 에서 prompt_loader.pack_dir_content_hash 로
    공용화(캐시·fail-closed 포함), 여기는 판정 모듈 위임만 남긴다.r   )pack_dir_content_hash)r;  rg  r=  )r@  rg  s     r   _pack_dir_content_hashrh  T  s     @ x88r'   c                *    t        t        |             S )ui  판정 팩의 **내용** 지문 — 버전 문자열이 아니라 로드되는 bytes.

    2026-08-08 확인: 판정 팩 v7 을 만들었는데 최종 스틸 311장 중 253장이
    재판정 없이 재사용됐다. 판정·수정 텍스트는 생성 프롬프트에 들어가지
    않아서, 버전 '문자열' 스탬프는 팩 내용이 바뀌어도 안 움직일 수 있다 —
    이 해시는 **이 프로세스가 실제 로드하는 디렉토리의 bytes** 를 지문에
    접는다. ★낡은 서버(옛 셀렉터를 든 메모리) 탐지책은 아니다(Codex
    HIGH-2): 낡은 프로세스는 옛 팩을 정직하게 해시해 옛 기록과 일치한다 —
    그건 "그 프로세스가 실제로 옛 팩으로 판정한다"는 뜻이라 지문으로는
    옳고, 스테일 서버 여부는 재기동 시각 확인으로 따로 잡아야 한다.
    )rh  r<  )r?  s    r   judge_pack_content_hashrj  \  s     ""<\"JKKr'   c                    | j                   j                  dd       | j                  | j                  dz         }|j	                  |       |j                  |        | S )u   tmp 생성→replace — write 도중 crash 로 truncated 파일이 '존재'로
    오인돼 skip 되는 창 제거 (Codex 2차 리뷰 B2).T)parentsexist_okz.tmp)parentmkdir	with_namerI   write_bytesr}   )out_pathdatatmps      r   atomic_write_bytesru  k  sQ     OO$6


X]]V3
4COODKKOr'   ))s   PNG

	image/png)s   z
image/jpeg)s   GIF87a	image/gif)s   GIF89arw  zTuple[Tuple[bytes, str], ...]_IMAGE_MAGICc                l    t         D ]  \  }}| j                  |      s|c S  | dd dk(  r	| dd dk(  ryy)	u\   바이트 내용으로 media type 판정 — 못 알아보면 png 로 둔다(기존 동작).Nr  s   RIFF      s   WEBPz
image/webprv  )rx  rV   )rs  magicmimes      r   _sniff_media_typer~    sD    #t??5!K $ BQx7tAbzW4r'   c                    t        | t        t        f      rt        |       j                         n| }t	        j
                  |      j                  d      }dddt        |       d| idS )u   이미지 소스(Path|bytes) → data URL 멀티모달 part.

    media type 은 **내용**으로 정한다 (`_sniff_media_type`). 이름이 `.png` 라도
    실제가 JPEG 이면 `image/jpeg` 로 나간다.
    ascii	image_urlurlzdata:z;base64,)r   r  )
isinstancer   r   
read_bytesbase64	b64encodedecoder~  )sourcers  b64s      r   r   r     st     fsDk* 	V!06 	 

4
 
'
'
0CU,T238C5A
 r'   judge_stillc           
        ddl m} ddlm} t	        |      }t
        j                  | t        |             }dj                   ||             } |t        ||||       |t        d|j                         |       |t        d|	      j                          |t        d
|	      j                          |t        d|	      j                         t        |d|      dS )u   multiroll_judge 팩 로드 — 후보 수 단어({count_word})만 포맷.

    v2 부터 명시 selector 로만 해석(암묵 latest-pack 승격 금지 — 팩
    디렉토리 추가만으로 전 소비자 판정 계약이 바뀌던 창 제거).
    r   )roll_labelsr3  , )
count_word
label_listr7  critique)count_word_lowerr7  fix_headr6  fix_tail	fix_labelfix_ref_label)	judge_syscritique_sysr  r  r  r  )r   r  r;  r4  r<  _COUNT_WORDSr   r   r<   r=  lowerr?   _load_optional)
roll_count
judge_namer?  r  r4  r@  wordr   s           r   resolve_judge_textsr    s     B5),7HJJ8DYY{:./F *&
 $*tzz|
  *h88=*h88= +x99> (o|5# r'   r  still_repair_regenz1.202608290400
regen_head
regen_tailregen_faults_headerc                H   ddl m} 	 t        t        |          } |t        d|      j                          |t        d	|      j                          |t        d
|      j                         dS # t        $ r' t        dt         d| dt        t               d      w xY w)uV  스틸 재생성 문안 셋 — head / tail / 결함 목록 머리말.

    ★fail-closed: 알 수 없는 selector 도, 없는 스템도 올린다. 빈 문자열로
     떨어지면 재생성이 **머리말도 꼬리말도 없이** 나가고 기록에는 이 팩으로
     남는다 — `fix_ref_label` 에서 이미 겪은 형태다.
    r   r3  zunknown z pack selector r_  r`  r  r6  r  r  r  )	r;  r4  STILL_REGEN_VERSION_MAPr   rb  rc  STILL_REGEN_MODULEr!   r?   r>  s      r   resolve_still_regen_textsr    s     6;*3|+<= "h@@E!h@@E* 5 #eg 	  ;)*/,9I J567q:; 	;;s   A1 10B!c                r    	 t        t        |       j                               S # t        t        f$ r Y yw xY w)us  팩 selector → 정수. 해석 불가는 -1(=도입 팩 미만으로 취급).

    -1 로 떨어뜨리는 쪽이 안전하다: 알 수 없는 selector 에 fail-closed 를
    걸면 그 팩의 정상 소비까지 막힌다. 반대로 필수 스템 결손을 놓치는 위험은
    `resolve_judge_pack_version` 이 selector 자체를 먼저 검증해 좁혀 준다.
    )r   r   r?   	TypeErrorrc  rd  s    r   _selector_numr    s6    3x=&&())z" s   !$ 66c                L   ddl m} t        j                  |      }|duxr t	        |      t	        |      k\  }	  |t
        ||       }|xs dj                         }|r|st        d|  d	|d
      |S # t        t        f$ r}|rt        d|  d|d      |Y d}~yd}~ww xY w)u  구 팩에는 없을 수 있는 스템. **도입 팩 이상에서는 fail-closed.**

    소비자는 빈 문자열을 "그 기능 없음"으로 읽는다. 구 selector 로 내렸을 때
    부팅이 깨지지 않게 하려는 것이지, 스템 결손·손상을 삼키려는 것이 아니다.

    삼키면 어떻게 되는지가 이번 결함의 형태다(Codex 리뷰 수용): 팩이 v7 인데
    `fix_ref_label` 이 없거나 읽히지 않으면 편집이 조용히 참조 0장으로
    떨어지고, **바로 그것을 고치려던 유료 수정이 옛 동작으로 실행된다.**
    기록에는 v7 이라 남는다. 그래서 도입 팩 이상에서는 막는다.
    r   r3  Nr6  u   multiroll_judge 팩 u    에 필수 스템 u4    이 없다 — 조용한 강등 금지(fail-closed)r6   u    의 u;    이 비어 있다 — 조용한 강등 금지(fail-closed))
r;  r4  _STEM_INTRODUCED_INr   r  r=  FileNotFoundErrorrb  rc  r?   )r@  rI   re  r4  required_fromrequiredr   r   s           r   r  r    s     6 (++D1MD( 2]."=1.2H<x@ JBD"8*E$ :7 89 	9 K x( &xj0CD8 L; <=BEF s   A5 5B#BB#c                    g }| D ]6  \  }}|j                  dd| d       |j                  t        |             8 |S )Nr   u   REFERENCE — r   )r   r   )r   r   re   r   s       r   r   r   )  sD    "$E"
sfug.FGHXc]# # Lr'   u   REFERENCE INDEX — the reference photographs above, numbered in the order they were given. Use these numbers, and only these, in `needs_ref_indices`:c                    t        | d      D cg c]  \  }\  }}| d|  }}}}|rdj                  |      nd}dt         d| dS c c}}}w )Nr    z. 
z(none were attached)r   r   )	enumerater<   rQ  )r   ire   _srclinesbodys         r   ref_index_partr  =  sq     &/|Q%?A%?!MUD s"UG_%? 
 A$499U*@D'8&9D6$BCCAs   Auj   OBSERVATIONS (from a separate visual inspector — verify each against the photograph before adopting it):c           	        |xs i j                  d      xs i j                  d      xs i j                  d      xs i }t        |j                  d      xs i       }g }| xs g D ]X  }t        |t              s|j	                  |r)|j                         D ci c]  \  }}||v s|| c}}n
t        |             Z d|dz   t        j                  |dd      z   d	S c c}}w )
u  관찰을 취합에 보여 주는 파트 — **취합이 받을 수 있는 칸만** 남긴다.

    ## 왜 거르나 — 실측 254 중 76건(30%)이 여기서 죽었다

    2026-08-27 DB 실측(14일):

        still_recipe_critique_compose   성공 178 · **실패 76**
        사유 1위 `schema violation: Additional properties are not allowed
                 ('severity' was unexpected)`

    관찰 스키마는 `{issue_ko, severity}` 를 내고 **`severity` 가
    required** 다. 그 JSON 을 통째로 취합 입력에 넣는데, GQ 취합
    스키마는 `{fix_en, issue_ko, needs_regeneration, unfixable}` 에
    `additionalProperties: False` 다.

    ★**모델은 입력에 있는 칸을 출력에도 넣는다.** 그게 자연스러운
     행동이고, 그때마다 스키마가 거부해 유료 왕복이 버려진다
     (`max_retry=1` 이라 재질의도 한 번 더 산다).

    ## 왜 스키마를 넓히지 않았나

    취합 스키마에 `severity` 를 받게 하면 GQ 경로 이슈에 severity 가
    실리기 시작하고, `multiroll_select` 의 심각도 게이트
    (`if any("severity" in issue …)`)가 **GQ 에서도 걸리기 시작한다.**
    지금 GQ 는 major 도 편집 대상인데 갑자기 critical 만 남는다 —
    ★거동이 통째로 바뀐다. 실측으로 확인한 것이다(2026-08-27:
     composition 이 돈 200샷이 전부 `main sev없음`).

    그래서 **입력을 계약에 맞춘다.** 안 받을 칸을 안 보여 주면 따라
    낼 일이 없고, 거동은 그대로다.

    ★허용 칸은 **스키마에서 읽는다.** 목록을 손으로 적으면 스키마가
     바뀔 때 조용히 어긋난다.
    rE  rF  r   r   r  Fr    )rL  indentr   )r   setr  r   r   r   rO  rT  )	observationscritique_schemaheaderpropsallowedkeptobsr  r   s	            r   observations_partr  K  s    N $")),7=2c(m"!33w<635 
599\*0b2G!#D!r!#t$ 	 '*iikBkdaQ'\QTkB$(I	/ " 

uQ!0 0  Cs   C"'C"c                ~    ddl }ddl}t        | |j                  j                  t
        |j                  t        f      S )u  운반층 오류인가 — 시간 초과·연결 끊김·HTTP 오류.

    ★**타입으로** 가른다. 오류 문구를 글자로 읽지 않는다(이 저장소 규칙).
     `urllib.error.HTTPError` 는 `URLError` 의 하위라 한 줄로 잡힌다.
    r   N)socketurllib.errorr  errorURLErrorTimeoutErrortimeoutConnectionError)r   r  urllibs      r   _is_transport_errorr    s5     cFLL11<"NNO= > >r'   multiroll_rollz16:9)
episode_idoperation_typeaspect_ratio	sanitizergemini_clientcontext_extrac           	         Fddl m} t        |dd      }|dk(  rddlm}	  |	       n |dk(  rddlm}
  |
       ndd	lm}  |       dd
d fd}d fd}ddl m} t        t        |dd            r|S |S )u  nb2 라벨드 참조 생성 gen_fn — moderation 시 sanitizer 1회 재시도.

    context_extra: set_context 에 병합할 추가 trace 필드(still_id 등) —
    llm_call_log 구조키 매칭(generation_call_id resolve)의 전제.

    still_safety_fallback_enabled=True 면 moderation 거부에 다단 후퇴
    사다리(2026-08-16 사용자 지시): 원 모델 재시도 → 교차 백엔드
    (Gemini↔Grok) → 연화 → 교차+연화. OFF(default)=기존 2회(원문→연화)
    byte-identical. 사다리가 삼키는 오류는 moderation 과
    GrokPromptOverBudget(무호출 비호환)뿐 — 그 밖의 오류·예산 소진은
    어느 단계든 즉시 전파(Codex BLOCK-3).
    r   r)   still_image_backendnb2grok2GrokImageClientgpt25)GptImageClientGeminiImageClientc                `    t        |       j                         }d|v xs d|v xs
 d|v xs d|v S )N
moderationsafetyblockedcontent_policy)r   r  )r   msgs     r   
_moderatedz#make_nb2_gen_fn.<locals>._moderated  sD    #hnnC ;8s? ;C;#3s#:	
r'   c                ,   |}dD ]  }	  j                   d
dd| ixs i  j                  ||D cg c]8  \  }}|t        |t        t        f      rt	        |      j                         n|f: c}}      \  }}	t        ||      c S  t        d	      c c}}w # t        $ rg}
|dk(  r\ |
      rTRj                  |t        |
      g |      }|j                  d      xs d}|rt        j                  d|        |}Y d }
~
 d }
~
ww xY w)N)r    r   
project_idr  r  multiroll_taglabeled_referencesr  r    sanitized_promptr6   u6   multiroll_gemini %s: moderation — sanitize 재시도unreachabler   )set_contextgenerate_imager  r   r   r  ru  r   sanitizer   r   r   r   )tagpromptr   rr  currentattemptre   ppng_msr   sr	sanitizedr  r  r  r  r  r  r  r  s                r   gen_fnzmake_nb2_gen_fn.<locals>.gen_fn  sU   G ))) )j#1 'E0CE )77
 )5	( )5HE1 &q3+6 a++-<=? )5	( ". 8 	S *(C88+ D =))+(  a<JsO	8M"++GSXr7KB "'9 : @bI (), #, s/   /B#=B6B#B##	D,ADDDc           
        ddl m} ddlm} |D cg c]8  \  }}|t	        |t
        t        f      rt        |      j                         n|f: }}}dfd}	dd}
d}d}t        |      |kD  rt        dt        |       d	| d
      d}d}d}d}d}|D ]  \  }}}|rN|L" nމ"j                  |t        |xs |      g d      }|j                  d      xs dj                         }|s n}|dk(  r| |	       }|}n|dk(  r| |
       }|}	 d| ixs i }|dk7  r|nd|d<    |j                  d! d| |j                  |r|n||      \  }}|dk7  rt        j!                  d| |       t#        ||      c S  |xs |xs t        d      c c}}w # |$ r  |$ r4}| |}t        j!                  d| |t        |      dd        Y d}~?d}~wt$        $ rz} |      s?|<|dk(  r7t'        |      r,|}t        j!                  d| |t        |      dd        Y d}~ |}|}t        j!                  d| |t        |      dd        Y d}~d}~ww xY w)u  SAFETY 다단 후퇴 — 단계: primary → 원 모델 재시도 → 교차 백엔드
        → **세 번째 백엔드(seedream)** → 연화(원 모델) → 연화(교차).
        연화 저작은 1회만, 교차·세 번째 클라이언트도 필요할 때 1회만 만든다.

        ★순서의 뜻 (2026-09-19 사용자 지시 「nb2 조합도 문제되면 seedream」):
         **제공자를 다 써 본 다음에야 글을 고친다.** 연화는 요청한 것을
         지운다 — 실측으로 앰버(10세)의 다친 모습이 세 판 모두 말짱한
         그림이 됐다. seedream 은 운반층이 아예 달라 막는 선도 다르고,
         그 한 장을 실제로 받아 냈다.

        비용 경계(Codex BLOCK-3): 사다리 **단계** 상한 6. 단계당 provider
        재시도는 클라이언트 규칙 그대로다 — moderation 은 내부 재시도 없이
        즉시 올라오고(정상 응답의 차단 사유), transient HTTP 만 최대
        llm_max_retries. 사다리가 삼키는 오류는 moderation 과
        GrokPromptOverBudget(무호출·결정론적 비호환) 둘뿐 — 그 밖의 오류와
        ImageCallBudgetExceeded(batch abort 계약)는 어느 단계든 즉시 전파.
        r   )ImageCallBudgetExceeded)GrokPromptOverBudgetc                 P    ddl m}  ddlm} t	        |      r |        S  |       S )Nr   r  r  )#app.modules.llm.gemini_image_clientr  !app.modules.llm.grok_image_clientr  r  )r  r  r  s     r   _cross_clientz=make_nb2_gen_fn.<locals>.gen_fn_ladder.<locals>._cross_client  s(     NI-9(**"$$r'   c                     ddl m}   | d      S )un  세 번째 검열 스택 — Seedream (2026-09-19 사용자 지시).

            ★「nb2 조합도 문제되면 seedream 으로」. 종전 사다리는
             Gemini↔Grok 둘뿐이라, 둘 다 막히면 **글을 부드럽게** 하는
             수밖에 없었다. 글을 고치면 요청한 것이 사라진다 — 실측:
             앰버(10세)의 다친 모습이 세 판 모두 말짱한 그림이 됐다.
             seedream 은 운반층이 아예 달라서(OpenRouter 전용 이미지
             엔드포인트) 막는 선도 다르다. 실측으로 그 한 장을 받아 냈다.
            r   build_cine_clientseedream)"app.modules.pipeline.cine_providerr  r  s    r   _third_clientz=make_nb2_gen_fn.<locals>.gen_fn_ladder.<locals>._third_client  s     M$Z00r'   ))primaryNF)same_model_retryNF)cross_backendcrossF)third_backendthirdF)r  NT)cross_sanitizedr  T   u4   safety ladder 단계가 승인 상한을 넘었다: z > ue   . 단계를 늘리려면 상한과 docstring 을 **같이** 고치고 비용 경계를 다시 본다.Nr    r  r6   r  r  r  r	  safety_ladderr  r  u9   multiroll_gemini %s: safety ladder %s 단계에서 회복uV   multiroll_gemini %s: safety ladder %s 단계 백엔드 비호환(%s) — 다음 단계   ua   multiroll_gemini %s: safety ladder %s 단계 대체 제공자 운반 오류(%s) — 다음 단계   uM   multiroll_gemini %s: safety ladder %s 단계 moderation(%s) — 다음 단계zsafety ladder: no attempt ran)r   r   r   )r   r  r  r  r  r   r   r  r"   r   r  r   r?   r  r  r   r   ru  r   r  )#r  r  r   rr  r  r  re   r  refsr  r  stages_LADDER_STAGE_CAPr  r  r  r   moderation_excstage	use_crossuse_sanitizedr  clientr  r  r  r   r  r  r  r  r  r  r  r  s#                              r   gen_fn_ladderz&make_nb2_gen_fn.<locals>.gen_fn_ladder  s)   $ 	GJ )
 )q %/C;%?T!W!QH( 	 
	%	1&
 v;**Fv;-s#4"5 6_`a a #'	(,.2/5+E9m!2$''C :(;RDVV$67=2DDF	 "FG#=)OEg%=)OEE$(#G-2E2G #i/ET o&""" <)j#1<5:< "00!.IF'+, 1 HS I%NN!"%u. *(C88I 06t  @ @>?	Ag
| +  ' S ")67:E3s8DS>S S  !$!# '2 )W 4 3C 8#&SC#8 !
 !$78;UHTcN$ $=!$s1   =FA.F
I&)GI!A I')IIstill_safety_fallback_enabledF)r   r   r   r   )rr  r   r   r   )
r9   r*   r:   r  r   app.modules.llm.gpt_image_clientr  r   r  r   )r  r  r  r  r  r  r  _sw_settings_backendr  r  r  r  r  _lad_settingsr  s   ```````        @r   make_nb2_gen_fnr"    s    , 
 	=<)>FwI+-M 
 H*,MM-/M
$* $*LhA hAT :GM#BEJKMr'   z5THE PROMPT (all candidates were generated from this):)project_configstep_tagopik_metadataprompt_headerc                   	
 ddl m t               t        |xs i       t	              		t
        k(  
	t        k(  }d
	
 fdt        j                  d      xs i j                  d      xs i j                  d      xs g       	 fd}||_	        t        dxs i j                  d	      xs g v       |_        |S )u8  prompt_header (seed 품질 2R): 변형 롤 모드는 후보들이 서로 다른 저작
    프롬프트로 생성되므로 기존 헤더 문구가 거짓 — 브리프용 헤더로 교체 가능.
    미지정=기존 문구 byte-identical.

    ★모델: 후보 선정 판정이므로 `resolve_select_judge_models()` 를 쓴다 —
    G+Q ON 이면 **Gemini + Qwen 동시**(합의 규칙 `_judge_gq`), OFF 는
    ANTHROPIC_API_KEY 가 있으면 Claude Opus + GPT-5.6 Sol 조건부 이중
    (`_judge_dual`/`combine_select_verdicts`), 없으면 `gemini-pro` 단독.r   call_structuredc           
     ,    | }| t         k(  rddlm}  |||      S | j                  t              r%ddlm}  |||| t        t              d  d      S i }t        k(  rddd}nr| t        k(  rd	di} 
||fi 	|d
| iid|S )Nr   ask_qwen_structuredr%  ask_openrouter_structured@  r   
max_tokensr%  F)enable_fallbacknum_retriesr3  r   r#  schema_namer%  )
rA   r=   r,  rV   rW   %app.modules.llm.openrouter_vlm_clientr/  r"   r]   rB   )r   r   
tag_suffixr  r,  r/  _seal_pair_qk_reversedbase_pcr)  judge_schemar  r%  r$  s          r   _onez"make_gemini_judge_fn.<locals>._one  s    
:,'$$ L&Y|+  34 -Y|C 789:
  +	 	B !#$$(-a@Ee{2 '.EE<
=g=sWe,<= 
 BG
 	
r'   rE  r   enumc                6   t        |      }t        |      t        |      k7  s0rOt        |      t              k7  st        |      t              k7  r!t        d|  dt        |       d| d d	      ddz   |z   dg}|t	        |      z  }t        ||      D ]7  \  }}|j                  dd	| d
d       |j                  t        |             9 
t        k7  r
t        k(  rt        	|t        |      d      S 
t        k(  rt        	|t        |            S 
t        k(  rt        	dz   |z   ||t        |            S 
t        k(  rt        	|t        |      d      S t        	|t        |            S  |i dd   ii      S )Nu   judge 계약 불일치[u   ] — 후보 u   장 · 라벨 u    · 스키마 라벨 u    (보내기 전에 막는다)r   r  r   r   r   qwen_priority)r   r   r   r5  )rY   r"   r  rc  r   r   r   r   rZ   r[   r   r\   r]   r   r_   r  )r  r  r   r   r   _labsr   r   r  r>  r:  _schema_labelsr<  r)  r=  r  ra   r%  r&  r$  s            r   judge_fnz&make_gemini_judge_fn.<locals>.judge_fn  s   Vu:Z(CJ#n2E$E'*5zS5H'H)#mC
O;L M 5n5E F//0 0
 !D(61(
 ' 	<((&*-FCLL&Jse12EFGLL!% . K (( ud6l0?A A ud6lCC(( 0D-$"6"? *d6l< < 00 ud6l0?A AvtUDLAAi<g <$wq	&:< '
 	
r'   r   r  )r   r   r8  r   r   r   )app.modules.llm.llm_clientr)  rL   r   rc   r[   r]   rY   r   
owns_orderr   emits_readings)r  r=  r#  r$  r%  r&  _cross_model_orderrD  r>  r:  r;  rC  r<  r)  ra   s   `` ```  @@@@@@@r   make_gemini_judge_fnrI    s    & ;(*F>'R(G #6*E,,L "22>
 >
F 


<
(
.B	3	3H	=	C	V	N/
 /
z -H #*//
;ArBDHOr'   structure_seed_ref_pick)r#  r$  r%  c                z    ddl m ddlmm i | xs i dt
        ii	 	 	 	 ddfd}|S )u  참조 사진 대비 **상대 비교** 판정을 multiroll JudgeFn 계약으로 잇는다.

    ★판정에 브리프를 주지 않는다. 지금까지는 브리프 대비 절대 점수였는데,
    참조 사진에 그 종류의 시설이 갖춘 것이 다 보여도 산출이 그것을 통째로
    빠뜨린 채 통과했다(실측: 참조는 실제 점포인데 산출은 빈 띠에 글자만).
    기준은 참조 사진 한 장이다.

    ★LLM 이 보는 칸은 승자와 사유 둘뿐이고, multiroll 이 요구하는
    {winner, ranking, verdicts} 는 코드가 채운다. 점수 칸을 주면 문턱이
    되살아나고, 칸을 늘릴수록 판정이 나빠진다.
    r   r(  )build_ref_pick_schemaload_ref_pick_systemr   c           	         | D cg c]
  }||k7  s	| }}||g|| D cg c]  }|||k(  rdnd||k(  r|ndd c}dS c c}w c c}w )u   승패를 1/0 으로만 옮긴다 — 정도를 매기지 않는다.

        좌우를 바꿔 두 번 물을 때(judge_flip) 두 번 다 이긴 쪽만 2점이 되고,
        한 번씩 이기면 동점이 돼 우선순위가 정한다.
        r    r   r6   )re   r   r   )r   r   r   r   )labsr   reasonr   rests        r   	_as_judgez)make_ref_pick_judge_fn.<locals>._as_judgei  s{      $5tsf}t5
  	  C "f}!!),vB@  		
 		
 6s   
??Ac           	        t        |      }|st        d      t        |      dk  r ||d   d      S t        |      }t	        ||      D ]6  \  }}|j                  d| dd       |j                  t        |             8   dj                  |      	      | |      
      }	t        |	j                  d      xs d      }
|
|vrt        d|
d| d       ||
t        |	j                  d      xs d            S )Nu$   ref_pick: 후보 라벨이 비었다r   r   u%   후보가 하나뿐 — 비교 없음r   r   r   r  )r  r5  r   r6   u,   ref_pick: 판정이 라벨 밖을 골랐다 u	    (가능 r`  	reason_ko)
rY   rc  r"   r   r   r   r   r<   r   r   )r  r  r   r   r   rO  r   r   r  rs  r   rR  rL  r)  rM  r%  pcr$  s              r   rD  z(make_ref_pick_judge_fn.<locals>.judge_fn|  s   F|CDDt9q=T47,STT&/&=$
+FCLL&SE)<=LL!% ,  DIIdO<!$'8'
 TXXh'-2. >vj	RVQWWXY[ [vs488K+@+FB'GHHr'   )rO  Sequence[str]r   r   rP  r   r   r   )rE  r)  (app.modules.pipeline.search_grounded_refrL  rM  rB   )	r#  r$  r%  rD  rR  rL  r)  rM  rU  s	    `` @@@@@r   make_ref_pick_judge_fnrX  P  sS    " ;
 
F^!r	EHw.D	EB

"0
&I I6 Or'   multiroll_critique)r#  r$  r%  ref_gatec                Z     ddl m i |xs i dt        ii fd}|S )Nr   r(  r   c           	         dd|z   dg}|t        |      z  }|j                  ddd       |j                  t        |             
r|j                  t        |              |	      S )Nr   THE PROMPT:
r   Photograph to examine:r5  r   r   r   r  )r  r  r   
image_pathr   r)  r  r  r%  rU  rZ  r$  s        r   critique_fnz,make_gemini_critique_fn.<locals>.critique_fn  s}    _v%=>'
 	<((f.FGHXj)*LL56lE?8'
 	
r'   )rE  r)  rB   )	r  r  r#  r$  r%  rZ  ra  r)  rU  s	   `` ``` @@r   make_gemini_critique_fnrb    s5     ;	E^!r	EHw.D	EB
 
 r'   )r#  r$  r%  r?  rZ  c                    
 ddl m ddlm
 ddlm} ddlm} t        |      } |t        d|       |t        d|       |        d	i |xs i d
t        ii
 fd}	|	S )uv  G+Q 수정 흐름 critique — Qwen 관찰 → Gemini 취합 2단 (2026-08-10 확정).

    CritiqueFn 시그니처 동일 — 하류(`_critique_and_fix` 의 issue partition·
    fix 조립·fix-rejudge·nb2)는 무변경이다. 현행 단일 critique 는 관찰과
    수정문 작성을 겸임하는데, 여기서는 역할을 가른다:

    1. **Qwen 관찰**: 무엇이 잘못됐는가만(수정문 없음 — 실측상 관찰·셈이
       강점, `matches_brief` 는 못 믿는 심판이라 관찰자 자리가 맞다).
    2. **관찰 0건 = 즉시 무결 반환** — Gemini 호출 생략(유료 1콜 절약).
       "Qwen 단독 관찰" 합의의 의미: Qwen 이 못 본 결함은 이 흐름에서
       고치지 않는다.
    3. **Gemini 취합**: 관찰을 이미지와 대조해 기각할 수 있고, 채택분만
       현행 critique 계약(issues: fix_en/unfixable/needs_regeneration)으로
       옮긴다 — 하류 소비 계약 불변.

    반환 dict 에 `qwen_observations`(관찰 원본)를 병기한다 —
    `record["critique"]` 에 접혀, 취합이 무엇을 기각했는지 대조로 추적된다.
    r   r(  r+  build_gq_observe_schemar3  gq_observe_sysr6  gq_compose_sys_composer   c           	        dd|z   dg}|t        |      z  }|j                  ddd       |j                  t        |              
 d|      }t        |j	                  d      xs g       }|sg g dS t        |      }r|j                  t        |             |j                  t        |              |	      }t        |      }	||	d
<   |	S )Nr   r]  r   r^  _observer-  r  )rF  qwen_observationsr5  rk  )r   r   r   rY   r   r  r  r   )r  r  r   r`  r   r  r  compose_partsrs  r   r,  r)  compose_syscompose_tagr  observe_schemaobserve_sysr%  rU  rZ  r$  s             r   ra  z(make_gq_critique_fn.<locals>.critique_fn  s    _v%=>'
 	<((f.FGHXj)*!j!;~'
 CGGN39r: r::U  !=> 	lO<	>m_;'

 4j#/ 
r'   rE  r)  r=   r,  r   re  r;  r4  r<  r=  rB   )r  r#  r$  r%  r?  rZ  re  r4  r@  ra  r,  r)  rm  rn  ro  rp  rU  s   ` `` `    @@@@@@@r   make_gq_critique_fnrr    s    6 ;CM5),7H&:K&:K,.NJh'K	H^!r	HK';1G	HB < r'   )r#  r%  r$  r?  rZ  c                    
 ddl m ddlm
 ddlm} ddlm} t        |      } |t        d|       |t        d|       |        d	i |xs i d
t        ii
 fd}	|	S )u  QK 수정 흐름 critique — Gemini 관찰 → Qwen 취합 2단.

    `make_gq_critique_fn` 과 같은 2단 계약(관찰 0건=즉시 무결 반환·취합이
    관찰을 이미지 대조로 기각 가능·하류 소비 계약 불변) — **자리를 반전**
    한다: 관찰(보조 자리)=Gemini(기존 G+Q 의 우선 모델), 취합(메인 자리)=
    Qwen(DashScope 직결). 2026-08-12 사용자 재결정 — 처음 설계(Kimi 관찰)
    는 OpenRouter 지연 실측(관찰 평균 310s)으로 보류.

    팩은 QK 도입판 v9(STAGING 관찰 축 포함)가 기본 — QK 는 새 체계라 물려
    받을 구 지문이 없고, GQ(v8 고정)의 지문은 1 byte 도 건드리지 않는다.

    반환 dict 에 `observer_observations`(관찰 원본)+`observer_model` 을
    병기한다 — 관찰자가 Qwen 이 아니므로 GQ 의 `qwen_observations` 키를
    재사용하지 않는다(기록 오독 방지).
    r   r(  r+  rd  r3  rf  r6  rg  rj  r   c           
        dd|z   dg}|t        |      z  }|j                  ddd       |j                  t        |              |d      }t        |j	                  d      xs g       }|s
g g t
        dS t        |      }r|j                  t        |             |j                  t        |               d	|
      }t        |      }	t        |	j	                  d      xs g       }
t        d |
D              rg }g }d}|
D ]  }|j	                  d      }t        |t              r&t        |t              sd|cxk  rt        |      k  sn |j                  |       \||   xs i j	                  d      }|r!|j	                  d      |k7  ri |d|i}|dz  }|j                  |        ||	d<   |r||	d<   |r||	d<   ||	d<   t
        |	d<   |	S )Nr   r]  r   r^  Fr#  r6  r%  r3  r  )rF  observer_observationsobserver_modelrh  r-  rF  c              3  F   K   | ]  }t        |t              sd |v   ywseverityNr  r   r   r  s     r   r   z;make_qk_critique_fn.<locals>.critique_fn.<locals>.<genexpr>P       F1*Q2EzQ   !	!r   observation_indexrz  r    compose_dropped_unboundcompose_severity_rebound_countrv  rw  )r   r   r   rY   r   rB   r  r  r   r^   r  r   r   r"   )r  r  r   r`  r   r  r  rl  rs  r   rF  bounddroppedreboundissueidxobs_sevr,  r)  rm  r  ro  rp  observe_tagr%  rU  rZ  r$  s                    r   ra  z(make_qk_critique_fn.<locals>.critique_fn)  s   _v%=>'
 	<((f.FGHXj)* e^;'

 CGGN39r: 2&13 3U  !=> 	lO<	>"j!;=
 4j cggh'-2.FFF*,E,.GGii 34!#s+z#t/D !S <3|+< <NN5)',277
Cuyy4?:u:j':EqLGU#   "CM18-.8?45'3#$ +
r'   rq  )r  r#  r%  r$  r?  rZ  re  r4  r@  ra  r,  r)  rm  ro  rp  r  rU  s   ` `` `    @@@@@@@r   make_qk_critique_fnr    s    0 ;CM5),7H&:K&:K,.NJh'K	H^!r	HK';1G	HB= =~ r'   c                Z   
 ddl m ddlm ddlm} ddlm} ddlm	 t        |      } |t        d|       |t        d	|       |       dfd

t        t        g| d| d| di |xs i dt        idt        ii
 fd}	|	S )u  G+G46 수정 흐름 critique — **양쪽 관찰**(Gemini+grok-4.6) → Gemini
    취합 2단 (2026-08-13 사용자 확정 "수정사항을 취합해서").

    GQ/QK 와 같은 2단 계약(관찰 0건=즉시 무결 반환·취합이 관찰을 이미지
    대조로 기각 가능·하류 소비 계약 불변)에서 **관찰자가 둘**이다:
    파일럿 실측(23샷)에서 두 관찰자의 강점이 달랐다 — grok 만 S64sh4
    좌석 물리를, 관찰 밀도는 gemini 가 앞섰다. 병합=관찰 합집합이라 어느
    한쪽 눈만으로는 못 잡던 결함이 취합 검증대에 오른다.

    취합자=Gemini 인 이유(동등 원칙과의 관계 명문): 취합은 승자를 정하는
    판정이 아니라 관찰을 사진과 대조해 기각·번역하는 역할이라 동등 계약
    밖이다. grok-4.6 취합은 결함 샷마다 왕복 ~80-140s·~$0.04 를 더 내는데
    파일럿에서 취합 품질 우위 근거가 없다. severity SOT 는 어차피 관찰자
    (재결속 코드가 강제)라 취합자 선택이 심각도를 좌우하지 않는다.

    계약 세부:
    - 병합 목록 = [Gemini 관찰..., grok 관찰...] (모델 슬롯 순서 고정) —
      compose 의 `observation_index` 와 severity 재결속이 **병합 목록**
      기준으로 왕복한다.
    - 한쪽 관찰 실패 = 생존 관찰로 진행 + `observer_failed` 기록 (판정
      `single_*` route 관례 동형 — 기록이 정직하면 강등이 사고가 아니다).
      양쪽 실패 = 마지막 원인을 달아 전파.
    - 관찰자·취합자 Gemini 호출은 enable_fallback=False 봉인 (QK BLOCK-2
      관례 — GPT 강등 판정이 Gemini 로 영속되는 오귀속 차단).
    - 반환 dict: `observer_observations`=병합 목록(인덱스 SOT),
      `observer_models`, `observer_counts`, (있을 때만) `observer_failed`.
      GQ(`qwen_observations`)·QK(`observer_model` 단수) 키와 구분된다.
    r   r(  r.  rd  r3  r)   rf  r6  rg  c                 ~    ddl m}  ddlm}  |       rj                  xs dj                         ry | ddd	      )
uZ  OpenRouter·grok 확인 — **부를 때** 건다 (2026-08-29 Codex BLOCK-1).

        종전에는 선정 resolver 가 이 열쇠까지 요구했는데, 2026-08-29 부터
        선정은 Gemini+GPT 라 그것을 안 쓴다. 안 쓰는 것을 거기서 요구하면
        정상 구성이 422 로 막힌다.

        ★그렇다고 **만드는 자리**로 옮기면 안 된다 — 이 팩토리는 수정
        단계가 꺼져 있어도 만들어진다(`still_recipe_service.py:1489`
        는 `gg46_judge_on` 으로만 감싼다). 그러면 같은 결함을 자리만
        옮겨 되풀이한다. 그래서 **실제로 관찰을 시작하기 직전**에 건다.

        조용히 Gemini 단독 관찰로 내려가지 않는다 — 「양쪽이 봤다」가
        거짓이 되고, 그 거짓 위에서 수정 여부가 정해진다.
        r   r.   r7   r6   Nz$multiroll.gg46_critique_unconfiguredu0  수정 관찰의 둘째 자리가 gpt-6-astra(high) 인데 OpenAI 키 또는 OPENAI_MODEL 이 비어 있다 — 키가 없으면 alias 가 Router 에 안 붙어 조용히 한쪽 눈으로 보고 「양쪽이 봤다」고 적게 된다. 키·모델을 채우거나 수정 단계를 내려라 (fail-closed)r0   r1   )r;   r/   rC   r8   rD   r?   )r/   r8   r*   s     r   _require_grok_slotz1make_gg46_critique_fn.<locals>._require_grok_slot  sE    & 	-7!6!6!<" C C E7W
 	
 		
r'   rj  
_observe_2rh  r   c                   dd|z   dgt        |      z  j                  ddd       j                  t        |                       i }g }d }d!"&'()+,f	d}ddlm} ddlm}	 dd	lm}
 dd
l	m
} ddlm}  |	 |
 | ||                        } |t        *            5 }*D ci c]  }||j                  ||       }}d d d        *D ]  }	 |   j                         ||<    t        |      t        *      k(  rt'        d      |g }*D ]#  }|j)                  |j+                  |g              % |t-        *      *D ci c]  }|t        |j+                  |g               c}d}|r||d<   |sdg i|S t-              }-r|j                  t/        |             |j                  t1        |%d              "$#|%,$+d      }t3        |      }t-        |j+                  d      xs g       }t5        d |D              rg }g }d}|D ]  }|j+                  d      }t7        |t8              r&t7        |t:              sd|cxk  rt        |      k  sn |j                  |       \||   xs i j+                  d      }|r!|j+                  d      |k7  ri |d|i}|dz  }|j                  |        ||d<   |r||d<   |r||d<   |j=                  |       |S c c}w # 1 sw Y   'xY w# t         $ r5}|}|j                  |       t"        j%                  d||       Y d }~ad }~ww xY wc c}w )Nr   r]  r   r^  c           
       	 | t         k(  r 	
d      }nM| j                  t              r  	| t        t              d  d      }n 	i 
d| iid      }t	        |j                  d      xs g       S )NFru  r0  r1  r   r  )rB   rV   rW   r"   rY   r   )r   r  r/  r)  ro  rp  observe_tag_gobserve_tag_xr%  r   rU  s     r   rj  z<make_gg46_critique_fn.<locals>.critique_fn.<locals>._observe  s    #%!;~#%="/
 !!"9:/!;~$; < =>#"/	 &!;~#Jb#J-'59I#J -"/	 /5266r'   r   rn   rp   rr   rt   rv   r   u:   G+G46 관찰: %s 실패 — 남은 관찰자로 진행: %ru   G+G46 관찰 전건 실패)rv  observer_modelsobserver_countsobserver_failedrF  u   OBSERVATIONS (concatenated from TWO independent visual inspectors — entries may report the same fault twice; verify each against the photograph before adopting it, and adopt each distinct fault at most once):)r  Fru  c              3  F   K   | ]  }t        |t              sd |v   ywry  r{  r|  s     r   r   z=make_gg46_critique_fn.<locals>.critique_fn.<locals>.<genexpr>D	  r}  r~  r  rz  r    r  r  )r   r   r   r   )r   r   r   r   ro   r   rq   r   rs   r   ru   r   rw   r"   r   r   r   r   r   r   extendr   rY   r  r  r   r^   r  r   r   rS  ).r  r  r   r`  r   r   r   rj  ro   rq   rs   ru   rw   r   r   rX   r   r   r   r  base_recordrl  rs  r   rF  r  r  r  r  r  r  r   r  r/  r)  rm  rn  r  ro  rp  r  r  r  r%  rU  rZ  s.                                  @r   ra  z*make_gg46_critique_fn.<locals>.critique_fn  s|   _v%=>'
 	<((f.FGHXj)*57	,0	7 	7L 	:B<A	
 %/&x0234  C,@AU9HIAQVQ//EI B$E #(<#6#6#8	%  % v;#o..;<(J-/$E	eR 89 % &2#O46E G6E3y}}Q+,,o G
 -3K)*b0K00U  !=> 	./%' 	( m_;'

 4j
 cggh'-2.FFF*,E,.GGii 34!#s+z#t/D !S <3|+< <NN5)',277
Cuyy4?:u:j':EqLGU#   "CM18-.8?45

;
O J BA
   e$P3     Gs<   -K32K.K3L #M.K33K= 	L>	*L99L>)r   None)rE  r)  r7  r/  r   re  r;  r4  r9   r*   r<  r=  rB   rE   )r  r#  r%  r$  r?  rZ  re  r4  r@  ra  r  r/  r)  rm  rn  ro  rp  r  r  r  rU  r*   s   ` `  `    @@@@@@@@@@@@r   make_gg46_critique_fnr  k  s    J ; N5(),7H&:K&:K,.N!
L #$89Oj)M  j
+MJh'K
/^!r 
/MG[3I
-
/BJ J JX r'   multiroll_gpt_compositionc                    	
 ddl m ddlm}  |t        dt        |            	i |xs i dt        ii
	 
fd}|S )u  GPT 구도 전담 critique 어댑터 (E2E11 fix③, 사용자 확정).

    CritiqueFn 시그니처 동일 — Gemini critique 와 별개로 선정본의 **구도
    위반만** 검사(sys 계약이 정체성/재질/조명 OUT OF SCOPE 봉인). 반환
    issues 는 Gemini issues 와 합산돼 하나의 수정 프롬프트로 i2i 된다.
    모델=GPT LVM(모델 분업: 이미지 검증·구도=GPT).
    r   r(  r3  gpt_composition_sysr6  r   c           	         dd|z   dg}|t        |      z  }|j                  ddd       |j                  t        |             
r|j                  t        |              |	      S )Nr   zTHE GENERATION CONTRACT:
r   zSelected photograph to inspect:r5  r_  )r  r  r   r`  r   r)  comp_sysr  r%  rU  rZ  r$  s        r   composition_fnz8make_gpt_composition_critique_fn.<locals>.composition_fnw	  s    1F:<'
 	<((%FG	IXj)* LL56h8'
 	
r'   )rE  r)  r;  r4  r=  r<  GPT_COMPOSITION_MODEL)r  r#  r$  r%  r?  rZ  r4  r  r)  r  rU  s   ` `` `  @@@r    make_gpt_composition_critique_fnr  ^	  sT      ;5+*<8H 
P^!r	OHw8M.N	OB
 
( r'   )r#   r   r   float)r   r   )ra   rV  r   r   )ri   r   rj   r   rk   r   r   r  )gemini_priorityF)
ra   r   r   r   r   r   r   r   r   r   )ra   r   r   r   r   r   r   r   )r   zDict[str, Dict[str, Any]]r   r   r   r   )ra   r   r   r   r   r   )r   r   )r?  r   r   Dict[str, str])r?  r   r   r   )re  r   r   r   )r@  r   r   r   )rr  r   rs  bytesr   r   )rs  r  r   r   )r  r   r   r   )r  r   r  r   r?  r   r   r  )re  r   r   r   )r@  r   rI   r   re  r   r   r   )r   Sequence[Tuple[str, Any]]r   r   )r   r  r   r   )r  zSequence[Dict[str, Any]]r  r   r  r   r   r   )r   BaseExceptionr   r   )r  r   r  zOptional[str]r  r   r  r   r  r   r  r   r  Optional[Dict[str, Any]]r   r   )r  r   r=  r   r#  r  r$  r   r%  r  r&  r   r   r   )r#  r  r$  r   r%  r  r   r   )r  r   r  r   r#  r  r$  r   r%  r  rZ  r   r   r   )r  r   r#  r  r$  r   r%  r  r?  r   rZ  r   r   r   )r  r   r#  r  r%  r  r$  r   r?  r   rZ  r   r   r   )^__doc__
__future__r   r  rO  loggingpathlibr   typingr   r   r   r   r	   r
   r   	getLogger__name__r   r=  rB   rH   rE   r   r  rA   r   GQ_SELECT_POLICY_VERSIONrW   QK_SELECT_POLICY_VERSIONGG46_SELECT_POLICY_VERSIONr   r&   rL   r[   r\   r]   r_   r`   rZ   rc   rl   r   r   r   r  r  r  r  ra  JUDGE_PACK_VERSIONSTILL_JUDGE_PACK_VERSIONFIX_REJUDGE_HEADER_PACK_VERSION%STILL_FIX_REJUDGE_HEADER_PACK_VERSIONGPT_COMPOSITION_PACK_VERSIONr  GQ_CRITIQUE_PACK_VERSIONQK_CRITIQUE_PACK_VERSIONGG46_CRITIQUE_PACK_VERSIONFIX_MISSING_PACK_VERSIONrA  rZ  r]  r<  rh  rj  ru  rx  __annotations__r~  r   r  r  r  r  STILL_REGEN_PACK_VERSIONSTILL_REGEN_STEMSr  r  r  r   rQ  r  _OBS_HEADERr  r  r"  rI  rX  rb  rr  r  r  r  r   r'   r   <module>r     s   #     G G G			8	$  #  "           4  (  A . @ T 9 !)K6r ! 
  0   $E  %6.3h!h'+h8FhVkk'*k!k k\p(p2;ppf!0#!0HM*K  Uw6fEO	O 	O" 	#O* 	+O< 	=OT 	UOl 	mOx 	yOB 	COX 	
YOf 	
gO| 	
}OL 	
MO^ 	
_O| 	
}O\ 	
]O x     "%  )- %  #    
    "     1* H 8  0B 
9 1C L/+ * $*### # 	#N ',  *   01  G  18
!H D+DD7  6*6#6 6 	6r
>  !%*.2JJ J 	J
 J J J ,J Jb 04%.2?oo !o -	o
 o ,o o oh 04-.2	G,G G ,	G
 G\ 04(.2 $ -	
  ,  D 04(.20G#G -G 	G
 ,G G G GZ 04.2(0e#e -e ,	e
 e e e eV 04.2(2p#p -p ,	p
 p p p pl 04/.24-#- -- 	-
 ,- - - -r'   