
    j-                        d Z ddlmZ ddlZddlmZmZmZmZ ddZ		 	 	 	 	 	 	 	 	 	 ddZ
ddZdZdd	Zdd
ZddZdddZedk(  r e e             y)u  최종 샷 한 장에 무엇이 영향을 미쳤나 — 세 자료원을 합쳐 한 화면으로.

    ./backend/.venv/bin/python -m tools.shot_influence <still_id 또는 shot_run_uid>

자료원 셋:
  · records.json  — 문안·참조·판정·선정·수리·지문
  · image_asset   — 최종 파일과 계보
  · Opik          — 실제 호출들(프롬프트 전문·토큰)

합치기는 순수 함수(`merge_shot_influence`)로 두고 조회는 밖에서 한다 —
시험이 망을 안 타게 하기 위해서다.
    )annotationsN)AnyDictListOptionalc                F    	 ddl m}  || xs i       S # t        $ r Y yw xY w)uZ  수리 산출이 최종이 됐는가 — **canonical 판정 하나**를 쓴다.

    `fix_stage_won`(`app.modules.pipeline.still_recipe`)이 자산 provenance 의
    단일 판정이다. 여기서 다시 구현하면 두 벌이 되어 갈린다.
    도구가 backend 없이 돌 때(요약 dict 만 넘겨받는 경우)는 False 로 둔다.
    r   )fix_stage_wonF)!app.modules.pipeline.still_reciper	   	Exception)recordr	   s     B/Users/manta/Documents/Projects/TheRoad-I1/tools/shot_influence.py_fix_appliedr      s+    CV\r** s    	  c           	     <   | xs i } | j                  d      xs, |xs i j                  d      xs |xs i j                  d      }g }g }| r|j                  d       |r;|j                  d       |j                  d      r|d   |k7  r|j                  d       |r;|j                  d       |j                  d      r|d   |k7  r|j                  d       i d|d|d|dt        | j                  d      xs g       d	| j                  d	      d
| j                  d
      d| j                  d      d| j                  d      dt        | j                  d      xs g       dt        | j                  d            dt	        |       d| j                  d      d| j                  d      d|xs i d|xs i j                  d      d|xs g D cg c]8  }|j                  d      |j                  d      xs i j                  d      d: c}S c c}w )uV   세 자료원을 uid 로 합친다. 없는 자료원은 조용히 빼고 표시한다.shot_run_uididrecords.jsonimage_assetopiksourcesuid_mismatchrefsref_modeselectedrankingtotalsverdictsfix_skippedfix_appliedfix_skip_reasoninput_fingerprintasset	thread_idcallsnameusagetotal_tokens)r$   tokens)getappendlistboolr   )r   	asset_rowopik_trace_row
opik_spansuidr   mismatchss           r   merge_shot_influencer2   "   sn    \rF::n% /B##N3/#((.  GH~&}%==(Y~-F#-MOOM*vd#t(<(COOF#7 	 	VZZ'-2.	
 	FJJz* 	FJJz* 	6::i( 	&**X& 	DJ/526 	tFJJ}56  	|F+!" 	6::&78#$ 	VZZ(;<%& 	b'( 	n*//<)* 	 !&B&
 ' UU6]g,"11.AC&
+ *
s   =Hc           
        g }|j                  d| j                  d              |j                  ddj                  | j                  d      xs g       xs d        | j                  d      r&|j                  ddj                  | d                 |j                  d	| j                  d
              |j                  d| j                  d              |j                  d       |j                  dt        | j                  d      xs g        d| j                  d       d       | j                  d      xs g D ]`  }|j                  d      xs d}|j                  d|j                  d              |j                  d| d|j                  d              b |j                  d       |j                  d       |j                  d| j                  d       d| j                  d       d| j                  d               | j                  d!      xs g D ]7  }|j                  d"|j                  d       d#|j                  d$              9 |j                  d       |j                  d%       | j                  d&      r|j                  d'       nF| j                  d(      r$|j                  d)| j                  d*              n|j                  d+       |j                  d       |j                  d,t        | j                  d-      xs g        d.       | j                  d-      xs g D ]@  }|j                  d/      }|j                  d|j                  d0       |rd1| ndz          B |j                  d       |j                  d2| j                  d3      xs i j                  d4              d5j                  |      S )6u   사람이 읽는 한 화면.u
   샷 uid : r   u   자료원 : z, r   u   없음r   u   ★uid 어긋남: u   주행    : r"   u   지문    : r     u&   ── 무엇이 들어갔나 (참조 r   u   건, 방식=r   u   ) ──asset_idu   자산 불명u     · labelz      asset_id=z  role=roleu!   ── 무엇이 골랐나 ──u
     선정: r   u      순위: r   u      점수: r   r   z    z: scoreu!   ── 무엇을 고쳤나 ──r   u+     수리 적용 — 수리 산출이 최종r   u     수리 건너뜀 — r   uK     수리 미적용 — 안 돌렸거나 재판정에서 원본이 이겼다u   ── 실제 호출 r#   u
   건 ──r'   r$   u
      토큰 u   최종 파일: r!   	file_path
)r)   r(   joinlen)mergedLraidvctoks          r   render_textrD   Y   sU   AHHz&**^4567HH|DIIfjj&;&ArBNhOPQzz.!	%dii~0F&G%HIJHH|FJJ{3456HH|FJJ':;<=>HHRLHH5c&**V:L:RPR6S5T Uzz*-.h8 9jj &B&eeJ2?	5w()*	?3%wquuV}o>? ' HHRLHH01HHz&**Z01VZZ	=R<S6::h/02 3jj$**	4g'r!%%.)9:; +HHRLHH01zz- 	>?	M	"	*6::6G+H*IJK
 	
^_HHRLHH$SG)<)B%C$DJOPjj!'R'eeHo	5v(#j,>2NO ( HHRLHH

7 3 9r>>{KLMN99Q<       c                    d}	  | d| d| dt                }|j                  d      xs g }|sy|D ]  }|j                  d      |k(  s|c S  |t         z  t        |j                  d      xs d	      k\  ry|dz  }{)
u<  uid 로 trace 를 찾는다 — **페이지 끝까지** 넘긴다.

    ★첫 페이지만 보면 200건 밖의 uid 는 자산이 있어도 Opik 이 빠진 채
    조용히 성공한다(2026-08-24 Codex BLOCK 3). 계약은 「임의의
    still_id·shot_run_uid」이므로 오래된 것도 찾아야 한다.
       z/v1/private/traces?project_id=&page=&size=contentNr   totalr   )	PAGE_SIZEr(   int)r(   pidr/   pagedrK   ts          r   _find_trace_by_uidrS      s     D
0fYK1 2%%	"(bAuuT{c!  )s155>#6Q77	 rE   c           
         g }d}	  | d| d| d| dt                }|j                  d      xs g }|s|S |j                  |       |t         z  t        |j                  d      xs d      k\  r|S |dz  }r)	uI   그 trace 의 span 전부 — 여기도 페이지를 끝까지 넘긴다.rH   z/v1/private/spans?project_id=z
&trace_id=rI   rJ   rK   rL   r   )rM   r(   extendrN   )r(   rO   r/   outrP   rQ   rK   s          r   _fetch_spansrW      s     "CD
/uJsefYK1 2%%	"(bJ

7)s155>#6Q77J	 rE   c                *   dd l ddlm j                  j	                  d      dj
                  id	fd} |d      }t        fd|j                  dg       D        d       }|sd g fS t        |||       }|d g fS |t        |||       fS )
Nr   )settings/zComet-Workspacec                    j                   j                   |        }t        j                  j                   j	                  |d            S )N)headers   )timeout)requestRequestjsonloadurlopen)pathreqbasehdrurllibs     r   _getz_fetch_opik.<locals>._get   sG    nn$$vdV_c$Byy//R/@AArE   z$/v1/private/projects?page=1&size=100c              3  L   K   | ]  }|d    j                   k(  r|d     yw)r$   r   N)opik_project_name).0prY   s     r   	<genexpr>z_fetch_opik.<locals>.<genexpr>   s/      <!9AV9 : :: $!9s   !$rK   )rd   strreturnDict[str, Any])
urllib.requestapp.core.configrY   opik_url_overriderstripopik_workspacenextr(   rS   rW   )	r/   ri   projsrO   rowrf   rg   rY   rh   s	        @@@@r   _fetch_opikrz      s    (%%,,S1Dh55
6CB 78E
 <9b!9 <=ACCRx
T3
,C
{RxT3,,,rE   c           
     H   dd l }|j                  d      }|j                  dd       |j                  |       }ddlm} ddlm}  |       }	 |j                  |      j                  |j                  d	k(        }|j                  |j                  |j                  k(        j                  |j                  j                               j!                         }|Td
|j                   d}	|j                  |j"                  j%                  |	            j                  |j                  j                               D ]F  }
	 t'        j(                  |
j"                  xs d      j+                  d      |j                  k(  r|
} nH ||j                  |j"                  j%                  d|j                   d            j                  |j                  j                               D ]F  }
	 t'        j(                  |
j"                  xs d      j+                  d      |j                  k(  r|
} nH d }d }|t'        j(                  |j"                  xs d      }|j0                  |j2                  |j4                  |j                  |j+                  d      d}ddlm}  ||j2                        j:                  dz  dz  }|j=                         rgt'        j(                  |j?                  d            }|j+                  d      xs |j                  tA        fd|jC                         D        d       }|xs i j+                  d      xs# |xs i j+                  d      xs |j                  }tE        |      \  }}|-|+tG        d|j                   d       	 |jI                          ytG        tK        tM        ||||                   	 |jI                          y# t,        t.        f$ r Y w xY w# t,        t.        f$ r Y #w xY w# |jI                          w xY w)Nr   u   최종 샷의 영향 요인)descriptionkeyu   still_id 또는 shot_run_uid)help)SessionLocal)
ImageAssetscenez%"shot_run_uid": "z"%z{}r   %)r   r9   generation_call_idstill_idr   )Pathreciper   zutf-8)encodingc              3  j   K   | ]*  }t        |t              r|j                  d       k(  r| , yw)r   N)
isinstancedictr(   )rl   rA   wants     r   rn   zmain.<locals>.<genexpr>  s2      O1"1d+n0E0M s   03'uV   ' 로 아무것도 못 찾았다 — still_id 인지 shot_run_uid 인지 확인할 것rH   )r   r,   r-   r.   )'argparseArgumentParseradd_argument
parse_argsapp.core.databaser   app.models.projectr   queryfilter
asset_typer   r}   order_by
created_atdescfirstpipeline_metadata_jsonlikera   loadsr(   	TypeError
ValueErrorr   r9   r   pathlibr   parentexists	read_textrw   valuesrz   printcloserD   r2   )argvr   apnsr   r   dbqry   r   candr,   r   metar   rjallrecr/   	trace_rowspansr   s                       @r   mainr      s   		 	 -J	 	KBOOE >O?	t	B.-	BE HHZ ''
(=(=(HIhhz**bff45>>!!&&(**/%' 	; (xr2D1166t<hz,,11345zz$"="="EFJJ*,/1vv6"6	5 {HH55::BFF81' (:0055789D!:: $ ; ; CtEEHS .F03566: #'C!	:9 	?::c88@DAD"vvCMM/2/E/E%(\\)-.)ACI %cmm$++h6GByy{BLL'L$BCxx/9266O O
 |  0 ;OSS0;46FF 	&s+	5;9,AbffX 6 7 8 	
 	k.Y$8 9 	: 

_ ":.  &z2 ! !B 	
s_   C1P AO#A(P ,AO9-E1P 0!P #O62P 5O66P 9PP PP P!__main__)r   rq   rp   r+   )
r   Optional[Dict[str, Any]]r,   r   r-   r   r.   zOptional[List[Dict[str, Any]]]rp   rq   )r=   rq   rp   ro   )rO   ro   r/   ro   rp   r   )rO   ro   r/   ro   rp   zList[Dict[str, Any]])r/   ro   rp   tuple)N)r   zOptional[List[str]]rp   rN   )__doc__
__future__r   ra   typingr   r   r   r   r   r2   rD   rM   rS   rW   rz   r   __name__
SystemExit rE   r   <module>r      s    #  , ,4$4 (4 -	4
 /4 4n,` 	, -,Pf z
TV
 rE   