
    j'                     \   d Z ddlZddlZddlZddlZddlmZmZm	Z	m
Z
  ej                  e      ZdZ	 d dedededed	ed
ede	eeef      fdZ	 d dededed	ed
ede	eeef      fdZdeeef   de
e   fdZdeeef   defdZdeeef   deeeeef   f   defdZ eh d      Zdeeef   defdZdefdZde	eeef      deeeeef   f   de	eeef      fdZ	 d dededed	ed
ede	eeef      fdZ	 d dddededed	ed
ededeeeeef   f   fdZdeeef   deeef   fdZy)!uI   Opik REST 에서 trace 를 시간 창으로 걷어온다 (읽기 전용).    N)AnyDictListOptional   kindbase_url	workspaceproject_namesinceuntilreturnc           
         g }d}	 t         j                  j                  |t        |t	        j
                  dddg      d      }t         j                  j                  | d|  d| d	|i
      }	t         j                  j                  |	d      5 }
t	        j                  |
j                               }ddd       j                  dg       }|snjd}|D ]4  }|j                  dd      }||k  rd} n||k  s$|j                  |       6 |s%|t        z  t        |j                  dd            k\  rn|dz  }6t        j                  d| t!        |      |       |S # 1 sw Y   xY w)u  `traces`/`spans` 공통 페이지 순회 — 기간 자르기 규칙은 하나뿐이다.

    start_time 이 [since, until) 인 행 전부 (최신순 페이지 순회).
    base_url 예: http://192.168.133.87:5173/api
    since/until: ISO 문자열 비교 (Opik start_time 은 ISO·UTC — 문자열
    비교가 시간 순서와 일치한다).

    ★**여기서 거르지 않는다.** 시험 기록 판별은 span 과 부모 trace 를
    **함께 봐야** 하므로(`select_call_rows`) 이 함수는 raw 로 준다.
       T
start_timeDESC)field	direction)r   sizepagesortingz/v1/private/?zComet-Workspace)headers<   )timeoutNcontentF totalr   u   %s %d건 수집 (since=%s))urllibparse	urlencode	PAGE_SIZEjsondumpsrequestRequesturlopenloadsreadgetappendintloggerinfolen)r   r	   r
   r   r   r   tracesr   qreqrdr   stoptsts                   Q/Users/manta/Documents/Projects/TheRoad-I1/tools/opik_prompt_audit/audit/fetch.py_fetch_pagedr9      sf   $ $&FD
LL""(zz'f=>@	$
  nn$$jTF!A3/&	2 % 4 ^^##C#4

1668$A 5%%	2&A|R(BEzEza   4)#s155!+<'==	5 6 KK,dCKGM# 54s   $EE(c                 "    t        d| ||||      S )u6   v1 호환 — 기존 소비처가 이것을 부른다.r0   r9   r	   r
   r   r   r   s        r8   fetch_tracesr=   @   s     (I|u& &    rowc                     | j                  d      xs g D ]4  }t        |t              s|j                  d      s&|t	        d      d c S  y)uH   `step:` 축 태그만 — 없으면 None. 이름으로 안 떨어진다.tagszstep:N)r*   
isinstancestr
startswithr/   )r?   tags     r8   	_step_tagrF   L   sG    %2%c3CNN7$;s7|}%% & r>   tracec                 `   t        |       }|r|S | j                  d      xs i }t        |t              r|j                  d      nd}t        |t              r&d|v r"|j                  d      d   j                         S t        |t              r|r|S t	        | j                  d      xs d      S )u   trace/span 의 스텝 이름.

    1순위 = `step:` 축 태그(v2). 2순위 = litellm 메타의 `trace_name` 마지막
    조각(v1 — 지난 자료를 계속 읽기 위해). 3순위 = 이름.
    metadata
trace_nameN>namer   )rF   r*   rB   dictrC   splitstrip)rG   rE   mdtns       r8   step_ofrS   T   s     E
C

	:		$"B!+B!5	4B"csbyxx}R &&(("cr	uyy 'C((r>   spantrace_indexc                     t        |       }|r|S |j                  t        | j                  d                  }|#t        |      }|r|S t        |      }|r|dk7  r|S t        |       S )u  호출(span) 하나의 스텝 이름.

    ① 자기 `step:` 태그 (v2)
    ② 부모 trace 의 스텝 (v1 — `{step}/{model}` 이름이나 `metadata.trace_name`)
    ③ 그래도 없으면 span 이름

    ★②를 ③보다 먼저 본다. `step_of` 를 먼저 부르면 span 이름으로 떨어져
    부모를 영영 안 본다 — litellm span 이름(`{model}_{obj}_{created}`)은
    **절대 비지 않기** 때문이다.
    trace_idr   )rF   r*   rC   rS   )rT   rU   rE   parentptagpnames         r8   step_of_callr[   f   sj     D/C

__S*!567F KUc\L4=r>   >   p-i2iproj-1pp1testc                     | j                  d      xs i }t        |t              sy|j                  d      }t        |t              r	|t        v ryd|vsd|vry|j                  d      dk(  xr | S )uP  시험이 남긴 기록인가 — 감사 셈에서 뺀다.

    2026-08-23 이전에는 돈 가드가 집 안 주소를 통과시켜 시험이 프로덕션
    프로젝트에 썼다. 지우지 않고 여기서 거른다 — 지우는 것은 되돌릴 수 없다.

    ★**이 그물은 완전하지 않다.** 두 갈래로 잡는다:

    1. 신원 없이 시간 0 — `record_provider_call` 을 scope 밖에서 부른 시험
    2. **가짜 신원** — `generation_context("p-i2i", …)` 안에서 부르면
       `project_id="p-i2i"` 가 실려 1번 그물을 **통과한다**

    그래도 못 잡는 것이 남는다(litellm 경로를 타는 시험 등). 그래서 리포트에
    **「걸러진 수」와 함께 「이 그물이 못 잡는 모양」**을 같이 적는다 —
    「0 건」이 「없다」로 읽히면 안 된다.
    rI   F
project_idTstepduration_msr   )r*   rB   rN   rC   _TEST_PROJECT_IDS)r?   rQ   pids      r8   is_test_originrg      st      
		"Bb$
&&
C#s'8 8R=266- A%1c'1r>   c                       y)uI   리포트에 함께 실을 한 줄 — 이 그물의 한계를 밝힌다.u   ★시험 기록 필터는 완전하지 않다 — 가짜 신원 목록에 없는 값이나 litellm 경로를 탄 시험 기록은 통과한다. 「걸러진 수」를 「오염 총량」으로 읽지 말 것. ri   r>   r8   test_filter_caveatrj      s    8r>   spansc                     g }| D ]V  }t        |      r|j                  t        |j                  d                  }|t        |      rF|j                  |       X |S )u   감사 대상 span 만 고른다 — **자기 또는 부모가 시험이면 뺀다**.

    span metadata 에는 `step` 이 없어서(`image_tracer` 가 trace 에만 넣는다)
    span 만 보면 시험 판별이 안 된다. 부모 trace 에는 있다. **둘을 함께 본다.**
    안 그러면 부모만 지워지고 자식 span 이 새 감사 SOT 에 그대로 들어가
    「시험 기록 0건」이 거짓 통과한다.

    ★부모를 못 찾은 span 은 **버리지 않는다** — 못 찾은 것과 시험인 것은 다르다.
    rW   )rg   r*   rC   r+   )rk   rU   outsprX   s        r8   select_call_rowsro      s\     !#C"RVVJ%7!89."8

2  Jr>   c                 "    t        d| ||||      S )u3  감사 대상 = **호출 하나당 한 행**. 그것은 언제나 span 이다. (raw)

    ★span 과 trace 를 그냥 합치면 **지난 자료를 두 번 센다.**
    `ImageTracer.log` 는 호출 하나마다 trace 를 만들고 그 밑에 span 도 만든다.
    litellm 도 `trace_id` 가 없으면 trace 를 만들고 span 은 **항상** 만든다.
    즉 v1 호출 한 건이 **trace 1 + span 1** 로 남아 있다.

    두 배가 된 숫자로는 「팩이 얼마나 커졌나」를 못 묻는다 — 이 도구의 존재
    이유가 사라진다. 그래서 **세는 것은 언제나 span** 이고 trace 는
    묶음·이름을 얻는 데만 쓴다(`fetch_trace_index`).

    ★인자·기간 계약은 `fetch_traces` 와 **같게** 둔다. 다르면 소비처가
    조용히 전 기간을 센다.
    rk   r;   r<   s        r8   fetch_spansrq      s    * 9lu& &r>      )	pad_hoursrs   c                :   ddl }	 |j                   j                  |j                  dd            }||j                  |      z
  j	                         }t        d| ||||      D 	ci c]  }	|	j                  d      r|	d   |	 c}	S # t
        $ r |}Y @w xY wc c}	w )ul  `trace_id` → trace. **세는 데는 안 쓴다** — 묶음·이름·시험 판별용.

    ★창을 `pad_hours` 만큼 **앞으로 넓힌다.** 창 경계 직전에 시작한 긴 trace 의
    창 안 span 이 부모를 잃으면, 시험 판별도 legacy 스텝 fallback 도 함께
    깨진다. 부모를 못 찾는 것이 여기서는 가장 나쁘다.
    r   NZz+00:00)hoursr0   id)datetimefromisoformatreplace	timedelta	isoformat
ValueErrorr9   r*   )
r	   r
   r   r   r   rs   _dt_swider6   s
             r8   fetch_trace_indexr      s     \\''c8(DES]]]33>>@ !-(I|T5!B  !B155; dGQJ !B    s   AB &BBBc                    ddd}| j                  d      }t        |t              ru|D ]n  }t        |t              s|j                  d      }||vr*|j                  d      }||xx   t        |t              r|nt        j                  |d      z  cc<   p |S t        |t              r&|j                  d      }t        |t              r||d	<   |S )
u   input 에서 role 별 텍스트 결합 (system/user). 비텍스트 파트는
    JSON 직렬화 길이로만 세지 않고 문자열화해 포함한다.r   )systemuserinputroler   F)ensure_asciipromptr   )r*   rB   listrN   rC   r#   r$   )rG   rm   inpmr   cr^   s          r8   messages_ofr      s     
$C
))G
C#tAa&55=D3i AIjC0djj7' 'I  J 
C	GGHaCKJr>   )9999)__doc__r#   loggingurllib.parser   urllib.requesttypingr   r   r   r   	getLogger__name__r-   r"   rC   r9   r=   rF   rS   r[   	frozensetre   boolrg   rj   ro   rq   r,   r   r   ri   r>   r8   <module>r      s   O     , ,			8	$	 0
00 0 	0
 0 0 
$sCx.0p 	&	&	& 	& 		&
 	& 
$sCx.	&4S> hsm )4S> )c )$tCH~ "3S#X#67<?8 DE 2S#X 24 2:9C 9Dc3h0 "&sDcN':";tCH~.8 &&& & 	&
 & 
$sCx.&<    	
   
#tCH~
6tCH~ $sCx. r>   