
    j&                         d Z ddlmZmZ ddlmZmZmZmZ ddl	m
Z
mZmZ dZdZdeeef   d	efd
Z	 ddeeeef      deeeeeef   f      d	eeeeef   f   fdZdeeeeef   f   d	eeeef      fdZy)u  발송 프롬프트 지표 — 전수 측정 (표본 외삽 없음).

Codex 검토(2026-08-15) 반영: 표본 8건 교집합 × 전체 호출 외삽을 버리고
전 호출을 직접 센다. '정적'은 줄 빈도(그 스텝 호출 중 해당 줄을 실은
비율) >= STATIC_DF 로 정의한다. 판정은 전부 구조 지표 — 어휘·의미 기반
판단 없음.

측정 정의(리포트 라벨과 일치해야 한다):
- 크기 단위는 전부 **자(Unicode code point)** — wire byte·token 아님.
- 같은 줄이 한 호출 안에 여러 번 있어도 1회로 센다(줄 존재 여부 기준).
- trace 1건 = 발송 1회로 세지 않는다 — provider retry 는 이 층에 없다.
    )Counterdefaultdict)AnyDictListOptional   )messages_ofstep_ofstep_of_callg?(   spanreturnc                     | j                  d      xs g }t        d |D        d      }| d| j                  d      xs d S )u3  스텝 **안에서** 「같은 모양의 호출」을 가르는 키.

    ★스텝만으로 묶으면 안 된다(2026-08-25 실측). 한 스텝 안에 성격이 다른
    호출이 섞이면 서로의 줄 빈도를 깎아 **어느 줄도 `STATIC_DF` 문턱을 못
    넘고, 정적분이 통째로 0 으로 잡힌다.**

    실측 두 건:
    - `scene_detail` 10호출 = 본 호출 6(pro) + 보조 판정 4(flash).
      6/10=60%, 4/10=40% 라 둘 다 탈락 → 0.00. 군집을 나눠 재니
      system 의 **99.1%** 가 정적이었다(재전송 196,152자).
    - `scene_image_pipeline`(발송량 54%) 105호출 = roll·judge·critique·
      fix·signage 등 **12종류 이상**. 역시 0.00 이었다.

    가르는 것은 `op:` 축 태그 + 모델이다 — 그 둘이 호출의 성격을 말한다.
    tagsc              3   n   K   | ]-  }t        |      j                  d       st        |      dd  / yw)zop:   N)str
startswith).0ts     S/Users/manta/Documents/Projects/TheRoad-I1/tools/opik_prompt_audit/audit/metrics.py	<genexpr>zcluster_of.<locals>.<genexpr>'   s,     D4a3q6+<+<U+Cs1vabz4s   55 |model)getnext)r   r   ops      r   
cluster_ofr       sI      88F!rD	D4Db	IBT488G$*+,,    Ntracestrace_indexc           
          i }| D ]  }|t        ||      n
t        |      }t        |      }|j                  |dddi d      }|dxx   dz  cc<   |d   j                  t	        |      dt               t               d      }|dxx   dz  cc<   dD ]a  }||   }	|| d	xx   t        |	      z  cc<   t        |	j                               }
|
j                  d
       || d   j                  |
       c  i }|j                         D ]k  \  }}|d   }|t        |d         d}dD ](  }|| d	   }|r||z  nd}d}d}i }|d   j                         D ]  }|d   }|dk  r|| d   j                         D cg c]  \  }}|t        |z  k\  r||f }}}t        d |D              }|||z  z  }|||dz
  z  z  }|D ]$  \  }}t        |j!                  |d      |      ||<   &  ||| d	<   ||| d<   |r||z  nd|| d<   ||| d<   |r|dk\  rt#        ||z  d      nd|| d<   t%        |j                         d       || d<   + |d   |d   z   |d<   |d   |d   z   |d<   |||<   n |S c c}}w )u  스텝별 전수 집계.

    ★`trace_index` 를 주면 행을 **span** 으로 보고 부모에서 스텝을 얻는다
    (2026-08-24). 안 주면 예전처럼 행 자체에서만 얻는다 — 지난 자료 호환.

    반환 row:
      calls, {system,user}_avg, {system,user}_total(전수 합),
      {system,user}_static_ratio(정적 줄 글자 합/평균 크기),
      {system,user}_static_lines([(줄, df)] 길이순),
      total_chars(sys+usr 전수 합)
    Nr   )callssystem_total
user_totalclustersr%   r	   r(   )r%   	system_dfuser_dfsystemuser_totalr   _df)r%   r(      c              3   8   K   | ]  \  }}t        |        y wNlen)r   line_s      r   r   zper_step.<locals>.<genexpr>b   s     <V'$CIVs   _avg_static_chars_resend_chars_static_ratioc                      t        | d          S )Nr   r3   )lcs    r   <lambda>zper_step.<locals>.<lambda>o   s    BqE
{r!   )key_static_linesr&   r'   total_charssystem_resend_charsuser_resend_charsresend_chars)r   r   r
   
setdefaultr    r   r4   set
splitlinesdiscardupdateitemsvalues	STATIC_DFsummaxr   roundsorted)r"   r#   accr   stepmsgsaclroletextlinesresultnrowtotalavgstatic_weightedresendmergedcnr5   cstaticcharss                           r   per_steprd   +   s+    &(C* Q,07
 	1~NN4Q"
 
 	
'
a
z]%%jm wy6
  	7q&D:Dfo#d)+)*EMM"$s|##E* ' , )+F99;agJ()s1Z=7IJ&Dfo&E !%1*qC  OF%'F
m**,[635cl3C3I3I3K 23Ka)b.0  )3K 2<V<<52:-%26**%GD!#&vzz$':A#>F4L  & - $)C4& !$C4&()1$q 4&&'*0C4&&'5:qAvo-q14 4&&'*0$:+<C4&&'5 '8 !03|3DDM"#89!$%8!9 :NtE F M)2s   =I:stepsc                 v   t        t              }| j                         D ]V  \  }}dD ]L  }|j                  | d      xs g D ]/  \  }}t	        |      t
        k\  s||   j                  |||f       1 N X g }|j                         D ]  \  }}|D 	
ch c]  \  }	}
}
|	
 }}	}
t	        |      dk  r*|j                  |t        |      t	        |      t        |D 
ch c]  \  }
}}
|
 c}}
      t	        |      t        d |D              z  d        |j                  d d       |S c c}
}	w c c}}
w )	uE  여러 스텝의 정적 줄에 동시에 나타나는 문장.

    role 을 보존한다 — 시나리오 전문처럼 여러 스텝이 **공유하는 입력
    데이터**(user)와 **중복 주입된 규칙**(system)은 다른 부류다. 실증:
    v1 이 줄거리 전문을 '중복 조각 2위'로 잡았다(Codex 검토). 의미 판단은
    도구가 하지 않는다 — role 조합을 표기해 읽는 사람이 가른다.

    노출량 = 줄 길이 × Σ(각 스텝에서 그 줄을 실제로 실은 호출 수 df) —
    표본 외삽이 아니라 전수 df 다.
    r+   r?   r0   c              3   (   K   | ]
  \  }}}|  y wr2    )r   r6   dfs      r   r   z(cross_step_duplicates.<locals>.<genexpr>   s     -FXQ2bs   )r5   re   
step_countrolesexposure_charsc                     | d   S )Nrl   rh   )ds    r   r=   z'cross_step_duplicates.<locals>.<lambda>   s
    A./r!   T)r>   reverse)
r   listrI   r   r4   CROSS_STEP_MIN_LINEappendrO   rL   sort)re   	line_hitsrQ   rZ   rU   r5   ri   dupshitssr6   step_setrs                r   cross_step_duplicatesrz   w   s7    "-T!2I[[]	c&DGGtfM$:;ArAbt9 33dO**D$+;< B ' #
 Doo'
d%)*T'!QAT*x=1H%h-d3d71aQd34!$i#-F-F*FF
 		 ( 	II/I>K + 4s   D/D5r2   )__doc__collectionsr   r   typingr   r   r   r   fetchr
   r   r   rK   rq   r   r    rd   rz   rh   r!   r   <module>r      s    - , , 5 5	 -T#s(^ - -, AEIT$sCx.) I"4T#s(^(;#<=IsDcN*+IX T#s(^#$ 	$sCx. r!   