
    i                       U d dl mZ 	 d dlZd dlZd dlZd dlZd dlZd dlZd dlZd dl	Z	d dl
m
Z
 d dlmZ d dlmZ ddlmZmZmZmZmZmZ ddlmZ dd	lmZmZmZmZmZmZ  ej>                  e       Z!g a"d
e#d<   d a$de#d<   da%de#d<   i dg ddg ddg ddg ddg ddg ddg ddg d d!g d"d#g d$d%g d&d'g d(d)g d*d+g d,d-g d.d/g d0d1g d2d3g iZ&d4e#d5<   g d6g d7g d8g d9g d:g d;g d<d=Z'd4e#d><   d?Z(d@Z)d\dAZ*d]dBZ+d^dCZ,d_dDZ-d`dEZ.dadbdFZ/dcdGZ0dddHZ1dedIZ2dfdJZ3dgdKZ4	 	 	 dh	 	 	 	 	 	 	 	 	 didLZ5djdMZ6dkdNZ7	 dl	 	 	 	 	 dmdOZ8	 	 	 	 dndPZ9dodQZ:	 dp	 	 	 	 	 	 	 dqdRZ;	 	 	 	 	 	 	 	 drdSZ<	 	 	 	 	 	 	 	 	 	 	 	 dsdTZ=dndUZ>dodVZ?	 dp	 	 	 	 	 	 	 dqdWZ@	 	 	 	 	 	 	 	 	 	 dtdXZAdudYZBdvdZZC	 	 	 	 	 	 	 dw	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dxd[ZDy)y    )annotationsN)datetime)Path)Optional   )_EXCLUDED_TYPES_RE_QUESTION_PATTERN_THREAD_GAP_MINUTES_extract_keywords_is_noise_message_mask_phone)ChatMessage)ALL_CATEGORIESBatchProgressInsightType	InsightV2Stage1ResultThreadV2	list[str]_recent_logsint_llm_call_count str_last_success_atu   보상/자동차)u	   자동차u   교통사고u   차량u   렌트u   대물u   대인u   자차u   보상/일반)u   보상u	   보험금u   청구u   지급u   실비u   입원u   통원u   보상/장기)u   장기보험u   종신u   연금   고지의무)u   고지u   알릴의무r   u   계약전알릴의무u   약관해석)   약관   조항   해석u   특약u   면책u   보장u   상품비교)u   상품u   비교   추천u   어디u   어떤보험u   태아보험u   언더라이팅)u   심사u   인수u   거절u   유보u	   부담보u   민원처리)u   민원u   분쟁u	   금감원u   소비자보호   손해사정)r!   u   손사u   사정u   조사u   의학지식)u   진단u   수술u   질병코드u   의학u   병원u   치료u   세금/절세)u   세금u   절세u   연말정산u   세액공제u   법률/판례)u   판례u   법원u   소송u   법률u   판결u   영업노하우)u   영업u   모집u   설계u   제안u   리크루팅u   고객관리)u   고객CSu   유지u   계약관리u   GA운영)GAu	   대리점u   지점u   점장u   수수료체계)u	   수수료u   인센티브u	   성과급FPu   디지털/IT)u   앱u	   디지털u	   온라인u	   플랫폼   기타zdict[str, list[str]]_CATEGORY_KEYWORDS_V2)u   질문u   궁금u   문의u   여쭤u   확인부탁u	   알고싶u	   어떻게)u
   제 경험u   실무에서u	   예전에u   제 생각에u   오래 해보니)u   사례u   사고u	   보상받u	   실제로u	   경험담u	   케이스)r   r   r   u   적용u   적용범위)u   팁u	   노하우u   방법u   이렇게 하면r    )u   개정u   변경u   시행u   신규u	   적용일)u   주의u   조심u   위험u   금지u   절대u	   하지마)qaexpert_opinioncase_analysisregulation_interpretationpractical_tipregulation_changewarning_INSIGHT_TYPE_PATTERNSu  당신은 보험 전문가 채팅 분석기입니다.
아래 대화 스레드를 분석하여 보험 실무 인사이트가 포함되어 있는지 판별하세요.

인사이트 유형 7가지:
1. qa — 질문 + 전문가 답변
2. expert_opinion — "제 경험상...", "실무에서는..." 같은 경험 공유
3. case_analysis — 실제 보험 사고/보상 사례 공유
4. regulation_interpretation — 약관 조항에 대한 해석/논쟁
5. practical_tip — 설계사 영업/업무 관련 노하우
6. regulation_change — 법/규정/고시 변경 정보
7. warning — "이런 건 주의하세요" 유형

JSON 응답:
{{
  "has_insight": true/false,
  "insight_types": ["qa", "expert_opinion"],
  "noise_reason": ""
}}

대화 스레드:
{thread_text}uW  당신은 보험 도메인 지식 정제 전문가입니다.
아래 대화 스레드에서 보험 실무 인사이트를 추출하세요.

## 인사이트 유형: {insight_types}

## 카테고리 목록
보상/자동차, 보상/일반, 보상/장기, 고지의무, 약관해석, 상품비교,
언더라이팅, 민원처리, 손해사정, 의학지식, 세금/절세, 법률/판례,
영업노하우, 고객관리, GA운영, 수수료체계, 디지털/IT, 기타

## 출력 형식 (JSON)
{{
  "title": "핵심 주제 50자 이내",
  "type": "qa|expert_opinion|case_analysis|regulation_interpretation|practical_tip|regulation_change|warning",
  "category": "카테고리 목록 중 하나",
  "summary": "핵심 내용 200자 이내 요약",
  "key_points": ["핵심 포인트 1", "핵심 포인트 2"],
  "expert": "가장 전문적 답변을 한 사용자 이름",
  "confidence": "high|medium|low",
  "related_topics": ["관련", "키워드"],
  "tags": ["#태그1", "#태그2"],
  "question": "Q&A 유형일 때 질문 요약",
  "answer": "Q&A 유형일 때 답변 요약"
}}

대화 스레드:
{thread_text}c                    t        j                  |       }| j                  D cg c]  }|j                          c}|d<   |S c c}w )u   ThreadV2(dataclass) → 직렬화 가능한 dict로 변환한다.

    messages 필드는 Pydantic ChatMessage이므로 model_dump()로 변환.
    나머지 필드는 dataclasses.asdict()의 기본 변환 사용.
    messages)dataclassesasdictr0   
model_dump)threaddms      M/home/jay/projects/insuwiki/scripts/kakao_knowledge/knowledge_extractor_v2.py_thread_to_dictr8      s<     	6"A-3__=Q\\^=AjMH >s   Ac                .   g }| D ]  }|j                  dg       D cg c]  }t        di | }}|j                  t        ||j                  dd      |j                  dd      |j                  d      |j                  dg                     |S c c}w )	u<   dict 리스트 → ThreadV2 리스트로 역직렬화한다.r0   
start_timer   topic_labelhas_insightinsight_types)r0   r:   r;   r<   r=    )getr   appendr   )datathreadsitemr6   msgss        r7   _threads_from_dictsrE      s     G 

*.((:r*BCQ a CC88L"5 HH]B7 HH]3"hh;	


 N Ds   Bc                b    | r|sy	 t        j                  |  d| d      S # t        $ r Y yw xY w)u9   날짜 + 시간 문자열을 datetime으로 변환한다.N z%Y-%m-%d %H:%M)r   strptime
ValueError)datetime_strs     r7   _parse_datetime_v2rL      s@    x  D68*!57GHH s   " 	..c                F    dj                  d | j                  D              S )u8   ThreadV2 → 전화번호 마스킹된 텍스트 변환.
c              3  f   K   | ])  }d |j                    dt        |j                          + yw)[] N)userr   content.0r6   s     r7   	<genexpr>z"_thread_to_text.<locals>.<genexpr>   s+     Uq;qyy#9":;Us   /1)joinr0   )r4   s    r7   _thread_to_textrX      s    99UV__UUU    c                   | j                         }t        j                  d|t        j                        }|r|j	                  d      j                         }	 t        j                  |      S # t
        j                  $ r Y nw xY w|j                  d      }|dk\  rZd}t        |t        |            D ]@  }||   dk(  r|dz  }||   dk(  s|dz  }|dk(  s%t        j                  |||dz          c S  t        j                  d|d      )uR   LLM 응답에서 JSON을 파싱한다. 코드블록 제거 및 JSON 객체 추출.z```(?:json)?\s*\n(.*?)\n\s*```r   {r   }u    JSON 객체를 찾을 수 없음)stripresearchDOTALLgroupjsonloadsJSONDecodeErrorfindrangelen)raw_texttext
code_blockbrace_startdepthis         r7   _parse_json_responsern      s   >>D<dBIINJ"((*zz$  ))C.Ka{CI. 	AAAw#~
aC
A:::d;Q&?@@	A 

A4
KKs   A- -BBc                   d}t        |       |kD  r&t        j                  dt        |       ||       | d| } ddd}|j                  ||      }	 t	        j
                  dd| d	|gd
d
|di t        j                  ddi      }|j                  dk7  rt        d|j                  dd        d}d}	|j                  }
t        |
      |kD  r%t        j                  dt        |
      |       |
d|	 }
t         dz  at#        j$                         j'                         a|
j+                         S # t        j                  $ rE}t        j                  d||t        |              t        d| d| dt        |        d      |d}~wt        $ rB}t        j                  d||t        |              t        d| dt        |        d      |d}~ww xY w)u   내부 Claude CLI로 LLM 호출.

    프롬프트는 stdin으로 전달하여 OS ARG_MAX 제한을 회피한다.
    500KB 초과 프롬프트는 절단 후 경고 로깅.
    i  uF   프롬프트 크기 초과: %d chars → %d chars로 절단 (model=%s)Nhaikusonnet)rp   rq   z/home/jay/.local/bin/claudez-pz--modelTz/tmp(CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC1)capture_outputri   timeoutcwdenvu8   Claude CLI 타임아웃 (%ds): model=%s, prompt=%d charsu   Claude CLI 타임아웃 (z
s): model=z	, prompt=z charsu4   Claude CLI OS 오류: %s (model=%s, prompt=%d chars)u   Claude CLI OS 오류: z	 (prompt=z chars)r   u   Claude CLI 오류:    i   i   uT   Claude CLI stdout 비정상적으로 큼: %d bytes (model=%s) — 앞 50KB만 사용r   )rg   loggerr-   r?   
subprocessrunosenvironTimeoutExpirederrorTimeoutErrorOSErrorRuntimeError
returncodestderrstdoutr   r   now	isoformatr   r]   )promptmodelru   _MAX_PROMPT	model_map	model_argresultexc_MAX_STDOUT_TRUNCATE_STDOUTr   s              r7   _call_clauder      s    K
6{[ TK		
 % I eU+I*D&)YOO2::OI3O
< A0t1D0EFGG!K ]]F
6{[ bK	

 ))*qO||~//1<<>M $$ 
FK		
 'y
5'3v;-W]^
	  
BK		
 $SE3v;-wG
	
s%   4D+ +G>A E>>G
=GGc                j    t         j                         D ]  \  }}|dk(  r|D ]  }|| v s|c c S   y)uJ   텍스트에서 v2 카테고리를 감지한다 (키워드 매핑 기반).r%   )r&   items)ri   categorykeywordskws       r7   _detect_category_v2r     sJ    399;  (x 	 BTz	   rY   c                     i t         j                         D ]$  \  }}t         fd|D              }|dkD  s ||<   & sdgS t        j	                         fdd      }|S )ua   텍스트에서 인사이트 유형을 감지한다 (키워드 기반, 매칭 수 기준 정렬).c              3  ,   K   | ]  }|v sd   ywr   Nr>   )rU   r   ri   s     r7   rV   z(_detect_insight_types.<locals>.<genexpr>&  s     ="*!=s   	r   r'   c                "    |    | dk(  rdfS dfS )Nr'   r   r   r>   )tscoreds    r7   <lambda>z'_detect_insight_types.<locals>.<lambda>.  s    vayqDy!8 a8 rY   T)keyreverse)r.   r   sumsortedkeys)ri   ityper   match_countsorted_typesr   s   `    @r7   _detect_insight_typesr   "  sq    F1779 (x=h==?'F5M( v8L
 rY   c                X    	 t        |       S # t        $ r t         j                  cY S w xY w)uF   문자열을 InsightType 열거형으로 변환. 실패 시 QA 반환.)r   rI   QA)type_strs    r7   _resolve_insight_typer   4  s+    8$$ ~~s   
 ))c                (   t        |       d|ddz  }|j                  j                  dd       |j                  dd      5 }t	        j
                  ||d	d
       ddd       t        j                  d||t        |             y# 1 sw Y   +xY w)u-   중간 결과를 JSON 파일로 저장한다.batch_03dz.jsonTparentsexist_okwutf-8encodingF   ensure_asciiindentNu*   배치 %d 중간 저장 완료: %s (%d건))	r   parentmkdiropenrb   dumpry   inforg   )
output_dir	batch_numresultspathfs        r7   _save_batchr   <  s    
yoU;;DKKdT2	3	) <Q		'15;<
KK<isSZ|\< <s   BBc                    dD ]A  }t        |       |z  }|j                         s"	 |j                          t        d| d       C y# t        $ r!}t
        j                  d||       Y d}~jd}~ww xY w)u9   정제 완료 후 체크포인트 파일을 정리한다.)checkpoint_threads.jsoncheckpoint_refined_threads.jsonu   체크포인트 정리: u    삭제u&   체크포인트 삭제 실패 (%s): %sN)r   existsunlink_add_logr   ry   r-   )r   namecpr   s       r7   _cleanup_checkpointsr   E  st    N T*$99;T		3D6ABT  TGsSSTs   A	A2A--A2c                	   | D cg c]!  }|j                   t        vrt        |      s|# }}|sg S g }t               }d}|D ]  }	|dz  }|rb|dz  dk(  rZt	        |t        |      z  dz        }
t        d| dt        |       d       t        |d|
d	| dt        |       ddddddd
       |j                  s<|j                  j                  |	       |	j                   d|	j                   |_        |j                  d   }d}d|	j                  v rd}nt        j                  |	j                        rd}n|	j                  |j                  k7  rd}net!        |j                  |j                        }t!        |	j                  |	j                        }|r#|r!||z
  j#                         dz  }|t$        k\  rd}|rd|j                  r|j                  |       t               }|j                  j                  |	       |	j                   d|	j                   |_        |j                  j                  |	        |j                  r|j                  |       |D cg c]  }t        |j                        dk\  s| }}|rS|r:t        dt        |       d       t        |dddt        |       ddddddd
       |r	 t'        |      dz  }|j(                  j+                  dd       |D cg c]  }t-        |       }}|j/                  t1        j2                  |dd             t        dt        |       d       t;        ||      }|r	 t'        |      dz  }|j(                  j+                  dd       |D cg c]  }t-        |       }}|j/                  t1        j2                  |dd             t        dt        |       d       |rCt        d t        |       d       t        |dd!d"t        |       d#dt        |      dddd
       t6        j=                  d$t        |             |S c c}w c c}w c c}w # t4        $ r/}t6        j9                  d|       t        d|        Y d}~:d}~ww xY wc c}w # t4        $ r.}t6        j9                  d|       t        d|        Y d}~d}~ww xY w)%u   개선된 스레드 분리.

    기존 규칙 (15분 gap, 날짜 변경, #궁금증 태그, 질문 패턴) +
    use_llm=True 시 메시지 20개씩 Haiku로 주제 연속성 판별.
    r   r         u   스레드 분리 중... (/u    메시지)runningu   스레드 분리 중 (statusprogresscurrentStepprocessedThreadstotalThreadsinsightsFoundnoiseFilterederrorsrG   F
   #궁금증T<   r   u$   LLM 스레드 정밀 분리 시작 (   개 스레드)u!   LLM 스레드 정밀 분리 중 (r   r   r   u)   스레드 분리 체크포인트 저장:    개 스레드u!   체크포인트 저장 실패: %su#   ⚠ 체크포인트 저장 실패: N)progress_filer   u*   LLM 정밀 분리 체크포인트 저장: u   스레드 분리 완료: 
   u   스레드 분리 완료 (   개)u   총 %d개 스레드 분리됨)typer   r   r   r   rg   r   _write_progressr0   r@   rJ   timer:   rS   r	   r_   rL   total_secondsr
   r   r   r   r8   
write_textrb   dumpsr   ry   r-   _llm_refine_thread_splitsr   )r0   use_llmr   r   r6   filteredrB   currentmsg_idxmsgpctprev_msgshould_splitprev_dtcurr_dtgap_minutesr   checkpoint_pathcheckpoint_datar   s                       r7   _split_threads_v2r   V  s    66(1B11E 	
H 
 	 G 
GG 4)1Wr\Q.wX.!34C0	3x=/UV' #%;G9Ac(m_T_#`()$%%&%&	 ##C($'HH:Qsxxj!9G##B' 3;;&L!((5LXX&L )FG(388<G7&0??ABF"55#'Lw'jG##C($'HH:Qsxxj!9G##C(i4)l w ":QS_%9q:G: ;CL>XY' !%Fs7|nTb#c()$%%&%&	 
F"&z"25N"N&&,,TD,I?F"G!?1#5"G"G**JJU1M DS\NR_`a ,G=Q
F"&z"25V"V&&,,TD,I?F"G!?1#5"G"G**JJU1M Ec'l^S`ab
 ,S\N-HI#!:3w<.M$% #G!"!"		
 KK/W>NIL ;. #H
  FBCH>seDEEF #H
  FBCH>seDEEFsg   &Q  Q>Q/Q 5Q
AQ /R R
AR 
Q 	R$RR
R 	S$SSc                   g }d}t        |      D ]  }||cxk  rt        | j                        k  s"n %t               }| j                  || |_        |dk(  r| j                  |_        nX|j                  r;|j                  d   j
                   d|j                  d   j                   |_        n| j                  |_        |j                  |       |} t               }| j                  |d |_        |j                  rd|dk(  r|s| j                  |_        n:|j                  d   j
                   d|j                  d   j                   |_        |j                  |       |S )u>   split_at 인덱스 배열에 따라 스레드를 분할한다.r   rG   N)r   rg   r0   r   r:   rJ   r   r@   )r4   split_indicessub_threadsprev_idxidxsubs         r7   _split_thread_atr     s6   "$KHm$ c0C00*C!??8C8CL1}!'!2!2$'LLO$8$8#93<<?;O;O:P!Q!'!2!2s#H *C??89-CL
||q=#..CN #Q 4 45Qs||A7K7K6LMCN3rY   c                &   t        | j                        dk\  rydj                  d | j                  D              }t        j	                         D cg c]  }|D ]  }|  }}}|D cg c]	  }||v s| }}t        |      dk(  S c c}}w c c}w )ua   메시지 3개 미만이면서 보험 키워드가 없는 스레드를 노이즈로 판별한다.   FrG   c              3  4   K   | ]  }|j                     y wNrS   rT   s     r7   rV   z#_is_noise_thread.<locals>.<genexpr>  s     <q<   r   )rg   r0   rW   r&   values)r4   	full_textkwsr   all_domain_keywordsmatcheds         r7   _is_noise_threadr    s    
6??q <FOO<<I)>)E)E)GV#RUVB2V2VV/Cb2?rCGCw<1 WCs   B*	B4Bc                ~	   | s| S d}d}g }t        dt        |       |      D ]  }t        ||z   t        |             }t        j	                  d|dz   |t        |              ||z  dz   }t        |       |z   dz
  |z  }t        d| d| d|dz    d	| dt        |        d
       |rWdt        |t        |       z  dz        z   }	t        |d|	d| d| d|dz    d| dt        |        ddt        |       dddd       | |||z    }
g }t        |
      D ]  \  }}|j                  }t        |      dk  r|}nt        |dd       t        |dd       z   }g }|D ]B  }|j                  dd j                  dd      }|j                  d|j                   d|        D t        |      dkD  r"|j                  ddt        |      dz
   d       dj!                  |      }|j                  d| d t        |       d!|         d"j!                  |      }|j#                  |#      }	 t%        |d$d%&      }t'        |      }|j)                  d'g       }|s1|j)                  d(d)gt        |
      z        }|D cg c]  }|g d*	 }}t        |
      D ]6  \  }}|t        |      k  r||   nd)g d*}t3        |j)                  d(d)            }t        |j)                  d/g             } |dk(  s|r|s2| rt5        ||       }!|j7                  |!       ~|j                  |       |d0   }"|"j                  j7                  |j                         | r[|"j                  }#|j9                          t;               }$|#|$_	        |"j<                  |$_        t5        |$|       }!|j7                  |!       t        j?                  d1||z   ||z   dz
         9 t        d||z  dz    dt        |       |z   dz
  |z   d2        t        d3t        |        d4       |D cg c]  }tA        |      r| }}|S c c}w # t*        $ r}t        j-                  d+|dz   |t/        |      j0                  |       t        d,||z  dz    d-t/        |      j0                   d.|        |
D cg c]  }d)g d*	 nc c}w }}Y d}~/d}~ww xY wc c}w )5u  Haiku를 사용하여 스레드 경계를 정밀 조정한다.

    연속된 스레드를 20개씩 묶어서 각 스레드가 이전 대화의 연속인지
    새 주제인지 판별(merge_with_prev)하고, 스레드 내부 주제 전환 지점
    (split_at)도 함께 처리한다. 노이즈 스레드(메시지 3개 미만 + 키워드 없음)는
    최종 결과에서 제거한다. merge_with_prev 배열 형식도 하위 호환 지원한다.
    u  아래는 규칙 기반으로 분리된 대화 스레드입니다.
각 스레드에 대해 두 가지를 판별하세요:

1. 이전 스레드와 같은 주제의 연속인가? → merge_with_prev
2. 이 스레드 내부에 주제 전환이 있는가? → split_at (메시지 인덱스 리스트)
   - 질문-답변이 끝난 후 새 질문이 시작되는 지점
   - 잡담/인사가 섞인 구간 이후 새 주제 시작 지점

스레드 목록:
{threads_text}

JSON 응답:
{{
  "threads": [
    {{"merge_with_prev": false, "split_at": []}},
    {{"merge_with_prev": true, "split_at": [5, 12]}},
    ...
  ]
}}
- 첫 번째 스레드는 merge_with_prev가 항상 false
- split_at이 [5, 12]이면 메시지 0~4, 5~11, 12~끝 으로 3개 스레드로 분리
r   r   u2   LLM 정밀 분리: 스레드 %d~%d / %d 처리 중r   u   LLM 정밀 분리 chunk r   u	    시작 (~u    스레드)r   r   u   LLM 정밀 분리 u    — 스레드 #~#z / u   개r   r   Nr  d   rN   rG   z  [rQ   z  ... (   u   개 생략)u   [스레드 u   ] (메시지 u   개)
z

)threads_textrp   x   r   ru   rB   merge_with_prevF)r  split_atu[   스레드 분리 LLM 정밀화 실패 (chunk %d~%d): %s(%s) — 규칙 기반 결과 사용u   ⚠ LLM 분리 chunk u	    오류: : r  r   u)   스레드 병합: chunk[%d] → chunk[%d]    완료u   ✅ LLM 정밀 분리 완료 (r   )!rf   rg   minry   r   r   r   r   	enumerater0   listrS   replacer@   rR   insertrW   formatr   rn   r?   	Exceptionr-   r   __name__boolr   extendpopr   r:   debugr  )%rB   r   refine_prompt
chunk_sizerefinedchunk_start	chunk_end	chunk_idxtotal_chunksr   chunkthreads_text_partsrm   r   rD   preview_msgslinesr6   	truncatedpreviewr  r   rh   parsedthread_decisionsmerge_flagsmfr   _r4   decisionshould_merger   r   prevcombined_msgscombined_threads%                                        r7   r   r     sy    M. J GQGj9 k
j0#g,?	@!OL		
  :-1	Gz1A5*D&yk<.	+XY/IZZ[\e[ffghklshtgu  vA  B	
 c;W5:;;C' #%7	{!L>Qabmpqbqarrtu~t  @C  DG  HO  DP  CQ  QT  $U()$'L%&%&	 kJ&>? )+e$ 	`DAq::D4yB##D!H~T"#Y?E! :IIdsO33D#>	s166("YK89: 4y2~Q'#d)a- DEii&G%%A3mCI;fU\T]&^_	` {{#56%%<%@	#F'3GH)(3F+1::i+D#$jj):UGc%j<PQFQ$@B;$  $$ #5) 	IAv s+,, !#).B? 
  -> FGL'+HLLR,H'IMAv\ "26="IKNN;/NN6*r{$$V__5 $(MMMKKM&.jO/<O,15O."2?M"RKNN;/?!O!Oa'5	> 	&{j'@1'D&EQGWaHadeHejtGtFuu|}	
Sk
Z -c'l^>JK!=Q)9!)<q=G=Ns$  	NNmaS	"" 'z(AA(E'FiPTUXPYPbPbOccefiejk EJ ?@Er:     	h >sD   %AP/5P*P/R:"R:*P//	R78A R2R%$R22R7c                   g }d}t        dt        |       |      D ]  }| |||z    }t        j                  d|dz   |t        |      z   t        |              t	        |d      D ]^  \  }}t        |      }|j                  |       |dz  dk(  s|t        |      k(  s9||z  dz   }t        d| d| dt        |       d	       ` ||z   t        |       k  st        j                  d
        |S )u   Haiku로 인사이트 여부 판별 (배치 20개씩).

    Returns:
        Stage1Result 리스트 (has_insight=True인 항목만 Stage 2로 진행)
    r   r   u!   Stage 1 배치 처리: %d~%d / %dr   r      배치 r  r   u    스레드 처리됨g      ?)
rf   rg   ry   r   r  _stage1_singler@   r   r   sleep)	rB   r   
batch_sizebatch_startbatchidx_in_batchr4   r   r   s	            r7   _stage1_filterrD    s    #%GJQGj9 kJ&>?/!O#e*$L		
 %.eQ$7 	a L&#F+FNN6"a1$E
(B':59	79+R~Qs5zlJ^_`	a #c'l2JJsO'* NrY   c                
   t        |       }t        j                  |      }	 t        |dd      }t	        |      }t        |j                  dd            }t        |j                  dg             }t        |j                  dd	            }|| _	        || _
        t        | |||
      S # t        $ rO}t        j                  d|       t        |       }	|	j                  | _	        |	j                  | _
        |	cY d}~S d}~ww xY w)u:   단일 스레드에 대해 Stage 1 필터를 수행한다.)thread_textrp   r  r  r<   Fr=   noise_reasonr   r4   r<   r=   rG  u9   Stage 1 LLM 실패: %s — 규칙 기반 필터로 전환N)rX   _STAGE1_PROMPT_TEMPLATEr  r   rn   r!  r?   r  r   r<   r=   r   r  ry   r-   _rule_based_filter_single)
r4   rF  r   rh   r2  r<   r=   rG  r   rule_results
             r7   r>  r>    s    !&)K$+++DFgsC%h/ M5!AB#'

?B(G#H

>2 >?(,#'%	
 	
  RTWX/7(44*88s   BB* *	D3AC=7D=Dc           	     h   | D cg c]  }|j                   s| }}t        j                  dt        |             g }t	        ||      D ]_  \  }}t        |||      }||j                  |       ||z
  dz   }	|	dz  dk(  s|	t        |      k(  sEt        d|	 dt        |       d       a |S c c}w )	us   Sonnet으로 심층 인사이트 추출.

    Stage 1에서 has_insight=True로 판정된 스레드만 처리.
    u7   Stage 2 진입: %d개 스레드 (Stage 1 필터 통과)startr   r   r   z	Stage 2: r   u    스레드 추출 완료)r<   ry   r   rg   r  _stage2_singler@   r   )
stage1_resultssource_chatstart_indexrinsight_candidates	extractedrm   s1entry
done_counts
             r7   _stage2_extractrY    s     &4Eq}}!EE
KKA
 I-[A 	2r1k2U#_q(
>Q*4F0G"GJ<q-?)@(AAYZ	 ' Fs
   B/B/c                   | j                   }t        |      }| j                  rdj                  | j                        nd}t        j                  ||      }	 t        |dd      }t        |      }t        ||||| j                        S # t        $ r9}	t        j                  d||	       t        |||| j                        cY d}	~	S d}	~	ww xY w)	u:   단일 스레드에 대해 Stage 2 추출을 수행한다., r'   )rF  r=   rq   i,  r  uH   Stage 2 LLM 실패 (스레드 %d): %s — 규칙 기반 추출로 전환N)r4   rX   r=   rW   _STAGE2_PROMPT_TEMPLATEr  r   rn   _build_insight_from_llmr  ry   r-   _rule_based_extract_single)
rV  indexrQ  r4   rF  insight_types_strr   rh   r2  r   s
             r7   rO  rO    s     YYF!&)K797G7G		""2"23T$++' , F
XhD%h/&FE;0@0@
 	
  XVX]_b	
 *&%bFVFVWW	Xs   1B 	C.CCCc                P   |j                   }t        | j                  dd            }|sZt        |      dkD  rL|dd }i |D ].  }j                  |j                  d      dz   |j                  <   0 rt        fd      }|j                  r|j                  j                  d      d   nd}	|D cg c]'  }d	|j                   d
t        |j                         ) }
}t        |D ch c]  }|j                   c}      }t        | j                  d|r|d   nd            }t        |      }t        | j                  dd            }|t        vr#dj                  d |D              }t        |      }t        d!i dd|ddt        | j                  d|d   j                  dd             d|d|dt        | j                  dd            dt        | j                  dg             d|dt        | j                  dd            dt        | j                  dg             dt        | j                  dg             d|	d|d|
d|dt        | j                  dd            d t        | j                  d d            }|j!                         S c c}w c c}w )"u<   LLM 파싱 결과로 InsightV2 딕셔너리를 생성한다.expertr   r   Nr   c                    |    S r  r>   u
user_counts    r7   r   z)_build_insight_from_llm.<locals>.<lambda>E  s    :a= rY   r   rG   rP   rQ   r   r'   r   r%   c              3  4   K   | ]  }|j                     y wr  r  rT   s     r7   rV   z*_build_insight_from_llm.<locals>.<genexpr>R  s     51QYY5r  idinsight-r   title2   summary
key_points
confidencemediumrelated_topicstagssource_daterQ  
raw_threadparticipantsquestionanswerr>   )r0   r   r?   rg   rR   maxr:   splitr   rS   r  r   r   rW   r   r   r3   )r2  r4   r_  rQ  fallback_typesrD   rb  answer_msgsr6   rs  rt  ru  r   insight_typer   r  insightrf  s                    @r7   r]  r]  3  s    ??D Hb)*Fc$i!m12h%'
 	?A!+!:Q!>Jqvv	?)@AF5;5F5F&##))#.q1BKBFGQAaffXRAII 678GJG.A./L 6::f>nQ&7tTUH(2L 6::j(34H~%HH555	&y1 eC[!&**Wd1goocr&:;<  	
 FJJy"-. 

<45  vzz,9: FJJ'7<= &**VR()      " VZZ
B/0  6::h+,!G$ ? H.s   5,J,J#c                >    | D cg c]  }t        |       c}S c c}w )u'   LLM 없을 때 규칙 기반 필터링.)rJ  )rB   r   s     r7   _rule_based_filterr  o  s    29:Q%a(:::s   c                n   | j                   }t        |      dk  rt        | dg d      S dj                  d |D              }t	        |      }t        d |D              r'd|v r|j                  d       |j                  d	d       t        |      d	kD  }|rd
nd}|| _        || _	        t        | |||      S )u   단일 스레드에 대해 규칙 기반 필터를 적용한다.

    최소 2개 이상 실질적 메시지가 있고, 인사이트 유형 패턴이 감지된 경우만 통과.
    r   Fu"   메시지 수 부족 (2개 미만)rH  rG   c              3  4   K   | ]  }|j                     y wr  r  rT   s     r7   rV   z,_rule_based_filter_single.<locals>.<genexpr>       1q1r  c              3  8   K   | ]  }d |j                   v   ywr   Nr  rT   s     r7   rV   z,_rule_based_filter_single.<locals>.<genexpr>       
3<199$
3   r'   r   r   u   인사이트 패턴 미감지)
r0   rg   r   rW   r   anyremover  r<   r=   )r4   rD   r  detected_typesr<   rG  s         r7   rJ  rJ  t  s    
 ??D 4y1}=	
 	
 1D11I*95N 
3d
33>!!!$'a&n%)K$2*IL$F)F$!	 rY   c                    | D cg c]  }|j                   s| }}t        j                  dt        |             g }t	        ||      D ];  \  }}t        |j                  |||j                        }|+|j                  |       = |S c c}w )u1   LLM 없을 때 규칙 기반 인사이트 추출.u%   규칙 기반 추출: %d개 스레드rM  )	r<   ry   r   rg   r  r^  r4   r=   r@   )	rP  rQ  rR  rS  
candidatesr   rm   rV  rW  s	            r7   _rule_based_extractr    s     ,=q}}!=J=
KK7ZIG:[9 "2*299abFVFVWNN5!" N >s
   BBc           	        | j                   }|syd}t        |      D ]  \  }}d|j                  v s|} n ||   }||dz   d }	dj                  d |D              }
t	        |
      }|j                  dd j                  dd      j                         }dj                  d	 |	D              }t        |j                        }d
}|	rEi |	D ].  }j                  |j                  d      dz   |j                  <   0 t        fd      }t        |
      }g }|	dd D ]Z  }|j                  j                  d      d   j                         }|s2t        |      dk\  sA|j                  t        |             \ |dz   |z   j                         }|dd }t        |D ch c]  }|j                   c}      }|D cg c]'  }d|j                   dt        |j                         ) }}| j                   r| j                   j                  d      d   nd
}|r|d   nd}t#        |      }g }t%        d |D              r|j                  d       |j                  d|j                  dd              t'        d+i dd|dd|d|d|d|d|r|n|gd|d d!d"|d#|d$|d%|d&|d'|d(|d)|dd* }|j)                         S c c}w c c}w ),u   단일 스레드에 대해 규칙 기반 추출을 수행한다.

    기존 _build_wiki_entry_rule_based와 유사하지만 InsightV2 모델에 맞게 확장.
    Nr   r   r   rG   c              3  4   K   | ]  }|j                     y wr  r  rT   s     r7   rV   z-_rule_based_extract_single.<locals>.<genexpr>  r  r  rl  rN   c              3  F   K   | ]  }t        |j                          y wr  )r   rS   rT   s     r7   rV   z-_rule_based_extract_single.<locals>.<genexpr>  s     HqK		2Hs   !r   c                    |    S r  r>   rd  s    r7   r   z,_rule_based_extract_single.<locals>.<lambda>  s    z!} rY   rg  r   r   rx   rP   rQ   r'   c              3  8   K   | ]  }d |j                   v   ywr  r  rT   s     r7   rV   z-_rule_based_extract_single.<locals>.<genexpr>  r  r  #r   r6  ri  rj  r   rk  r   r   rm  rn  rb  ro  rp  rq  rr  rs  rQ  rt  ru  rv  rw  i  r>   )r0   r  rS   rW   r   r  r]   r   r?   rR   rx  r   ry  rg   r@   r  r:   r   r  r   r3   )r4   r_  rQ  r=   rD   question_idxrm   r6   question_msgr{  r  r   rk  answer_textquestion_textrb  r   rn  
first_linesummary_rawrm  ru  rt  rs  r   r|  rr  r}  rf  s                               @r7   r^  r^    s\    ??D L$ 1199$L
 %L|a')*K 1D11I #9-H   "%--dC8>>@E ))HKHHK   4 45M F%'
 	?A!+!:Q!>Jqvv	?Z%<= !+H J!_ 7YY__T*1-335
#j/R/k*567 !3&4;;=K$3G .A./L CGGQAaffXRAII 678GJG5;5F5F&##))#.q1BK $1}QdH(2L D

3d
33L!KK!H$$S#./01 eC[!  	
  ",:%           "   4C !G$ G / Hs   K<,Kc                    t        j                         j                  d      }t        j	                  d| d|         t        dd ay)uA   로그 메시지를 _recent_logs에 추가 (최근 15줄 유지).z%H:%M:%SrP   rQ   iN)r   r   strftimer   r@   )r   tss     r7   r   r     s@     
	 	 	,B!B4r#(%LrY   c                D   	 t        |       }|j                         r^	 t        j                  |j	                  d            }|j                  d      dk(  ry|j                         }|j                  |       |}t        j                         j                         |d<   t        |d<   t        |d<   t        t              |d	<   |j!                  t        j"                  |d
d      d       y# t        $ r Y ~w xY w# t        $ r t$        j'                  d|        Y yw xY w)u2   진행률 파일에 현재 상태를 기록한다.r   r   r   	cancelledNlastUpdatedllmCallCountlastSuccessAt
recentLogsFr   r   u   progress file 쓰기 실패: %s)r   r   rb   rc   	read_textr?   copyupdater  r   r   r   r   r   r  r   r   r   ry   r-   )r   rA   pr   mergeds        r7   r   r   "  s    I88:	**Q[['[%BC;;x(K7 d# 'lln668].^ 0_!,/\	TZZ5CgV    I8-HIs;   C= 9C. #C. ;A2C= .	C:7C= 9C::C= =DDc                Z   da da|r-t        |       }|j                  d      rK|dd }	| D 
cg c]:  }
|
j                  j                  |	      rt        |
j                  dd       dk  r|
< } }
n|j                  d	      rK|dd }	| D 
cg c]:  }
|
j                  j                  |	      rt        |
j                  dd       d
k\  r|
< } }
n+| D 
cg c]   }
|
j                  j                  |      s|
" } }
t        j                  d|t        |       |       t        d| dt        |        d| d       |sUt        j                  d       t        d       g }d}|rdt        |      dz  }|j                         rFt        t        j                  |j                                     }t        dt        |       d       d}|st!        | d||      }|dkD  r||d }t#        |      }t%        ||      }t        j                  dt        |             t        dt        |       d       |rt'        |       |rQt)        |dddt        |      t        |      t        |      ddt+        j,                         j/                         d	       |S t        j                  d       t0        j3                          t        d       t+        j,                         j/                         }g }d}|rt        |      d z  }t        |      dz  }|j                         rGt        t        j                  |j                                     }t        d!t        |       d       d}nV|j                         rFt        t        j                  |j                                     }t        dt        |       d       d}|st!        | d||      }t        |      }|dkD  r.t        j                  d"||       t        d#| d$| d       ||d }t5        |%      }||_        g }|rNt)        |d&t9        dt        |t9        |d'      z  d(z        dz         |dkD  r	d)| d*| d+nd)| d||ddd|d	       d}d}	 t;        t=        dt        |      |      d',      D ]-  \  }}||||z    }|r.|d   j>                  r|d   j>                  d   j@                  dd- nd}t        j                  d.||d'z   |t        |      z   t        |             t        d/| d0|d'z    d1|t        |      z    d2       g }	 t        d/| d3|d'z    d1|t        |      z    d4t        |       d5	       |rt        |      |z   d'z
  |z  }dt        |j6                  t9        |jB                  d'      z  d(z        z   }t)        |d&|d/| d6| d7|d'z    d1|t        |      z    d2	|j6                  |jB                  |jD                  |jF                  |jH                  ||d8
       tK        |      }|xjF                  tM        d9 |D              z  c_#        tO        ||t        |      d'z   :      }|xjD                  t        |      z  c_"        |jQ                  |       d}d}|xj6                  t        |      z  c_        |r|rt[        |||       ~~t]        j^                          ||z   t        |      k  rta        jb                  d'       t        j                  dD||j6                  |jB                  |jD                  |jF                  |jH                         t        d/| dE|jD                   dF|jF                   dG       |st        |      |z   d'z
  |z  }dt        |j6                  t9        |jB                  d'      z  d(z        z   }g }#||z   }$|$t        |      k  r|||$|$|z    }%|%rr|%d   j>                  rc|%d   j>                  dd? D ]N  }&|&j@                  ddH }'t        |&j@                        dHkD  r|'dIz  }'|#je                  |&jf                   dJ|'        P |#rdKji                  |#      nd}(|rd/| d6| dL| d2nd/| d6| dM})d&||)|j6                  |jB                  |jD                  |jF                  |jH                  ||(|dN}*|r||*dO<   t)        ||*       0 	 t        j                  dW|jD                  |jF                  |jH                         t        dX|jD                   dY|jF                   d       |rt'        |       |rHt)        |ddd|j6                  |jB                  |jD                  |jF                  |jH                  |d	       |S c c}
w c c}
w c c}
w # tR        $ r}tU        |      jV                  }|}t        jY                  d;||       t        d<| d=       |xjH                  d'z  c_$        |d'z  }	 t#        |      } t%        | |t        |      d'z   :      }!|xjD                  t        |!      z  c_"        |jQ                  |!       |!}n,# tR        $ r }"t        jY                  d>|"       Y d}"~"nd}"~"ww xY w|d?k\  rt        jY                  d@|t        |             t        dA| d2       |xj6                  t        |      z  c_        |ryt)        |dBt        |j6                  t9        |jB                  d'      z  dz        dC| d2|j6                  |jB                  |jD                  |jF                  |jH                  |d	       |cY d}~|xj6                  t        |      z  c_        c S Y d}~d}~ww xY w# |xj6                  t        |      z  c_        w xY w# tR        $ r}+tU        |+      jV                  },t        jY                  dP|+dQ       	 ddl5}-|-jm                  |-jn                        jp                  dRz  }.t        jY                  dS|.       n# tR        $ r Y nw xY wt        dT|+        |r~t)        |dBt        |j6                  t9        |jB                  d'      z  dz        dU|, dV|+ |j6                  |jB                  |jD                  |jF                  |jH                  d'z   |d	        d}+~+ww xY w)Zu  Phase 1 다층 LLM 파이프라인으로 인사이트 추출.

    Parameters
    ----------
    messages:
        parse_kakao_chat()으로부터 얻은 ChatMessage 리스트
    use_llm:
        True이면 Claude CLI를 통한 Haiku+Sonnet 2단계, False이면 규칙 기반
    source_chat:
        채팅방 이름
    output_dir:
        중간 결과 저장 디렉토리. 지정 시 배치마다 batch_NNN.json 저장
    batch_size:
        배치 크기 (기본 50 스레드씩)
    progress_file:
        진행 상태를 기록할 JSON 파일 경로. None이면 기록하지 않음.
        배치마다 갱신되며 대시보드 서버가 이 파일을 읽어 진행률을 표시한다.
    month:
        필터링할 월 (YYYY-MM 형식, 예: "2026-03"). 빈 문자열이면 전체 기간 처리.
    skip_threads:
        처음 N개 스레드를 건너뜁니다. 중단된 정제를 이어서 실행할 때 사용.

    Returns
    -------
    InsightV2.model_dump() 딕셔너리 리스트
    r   r   z-H1Nr     r      z-H2   u1   월 필터: %s → %d개 메시지 (원본 %d개)u   월 필터링 적용: u    → u   개 메시지 (원본 r   u   규칙 기반 모드로 실행Fr   u0   체크포인트 복원: 스레드 분리 결과 r   T)r   r   r   u"   규칙 기반 추출 완료: %d건u   규칙 기반 추출 완료: u   건	completedr  u   완료)	r   r   r   r   r   r   r   r   	startedAtu!   LLM 모드로 실행 (Claude CLI)u   정제 프로세스 시작r   u1   체크포인트 복원: LLM 정밀 분리 결과 uA   이어서 정제: 처음 %d개 스레드 건너뜀 (전체 %d개)u   이어서 정제: u    개 스레드 건너뜀 (전체 )total_threadsr   r   Z   u    배치 처리 준비 (스레드 u   개, u   개 건너뜀)rM  rl  u-   배치 %d 처리 시작: 스레드 %d~%d / %dr=  u    처리 시작 (스레드 #r  )u#    LLM 분석 요청 중 (스레드 #r[  u   개 스레드)...r   u     — LLM 분석 중 (스레드 #)
r   r   r   r   r   r   r   r   r  currentThreadNamec              3  :   K   | ]  }|j                   rd   ywr   )r<   )rU   rS  s     r7   rV   z'extract_knowledge_v2.<locals>.<genexpr>   s      /Q]]A/s   )rR  uH   배치 %d 처리 오류: %s — 해당 배치 규칙 기반으로 전환u   ⚠ 배치 u$    오류 - 규칙 기반으로 전환u4   규칙 기반 폴백도 실패: %s — 배치 스킵r  uu   연속 3회 배치 실패로 안전 중단 (마지막 오류 유형: %s). 지금까지 수집한 결과 %d건 반환.u8   ❌ 연속 3회 배치 실패로 안전 중단 (오류: failedu;   연속 3회 배치 실패로 안전 중단 (오류 유형: uP   배치 %d 완료 | 처리: %d/%d | 인사이트: %d | 노이즈: %d | 오류: %du    완료: 인사이트 u   건, 노이즈 u
   건 필터   z...r  z | u    완료 (오류: r  )r   r   r   r   r   r   r   r   r  currentPreviewr  lastBatchErrorTypeu)   extract_knowledge_v2 치명적 오류: %s)exc_infoi   u   메모리 사용량: %.1fMBu   ❌ 치명적 오류: u   오류 발생 (z): u_   extract_knowledge_v2 완료 | 총 인사이트: %d건 | 노이즈 필터: %d건 | 오류: %d건u%   ✅ 정제 완료! 총 인사이트: u   건, 노이즈 필터: )9r   r   rg   endswithrJ   
startswithr   ry   r   r   r   r   rE   rb   rc   r  r   r  r  r   r   r   r   r   r   clearr   processed_threadsrx  r  rf   r0   rS   r  insights_foundnoise_filteredr   rD  r   rY  r"  r  r   r   r   r   gccollectr   r?  r@   rR   rW   resource	getrusageRUSAGE_SELF	ru_maxrss)/r0   r   rQ  r   r@  r   monthskip_threadsoriginal_countbaser6   rB   restored
threads_cprP  all_resultsr:   
skip_split
refined_cporiginal_totalr   consecutive_errors_last_batch_error_typer   rA  batch_threads_cur_thread_namebatch_resultstotal_batchesr   r   exc_typefallback_stage1fallback_resultsfallback_excpreview_lines
next_start
next_batchr   ri   current_preview_step_label_progress_payload	fatal_excfatal_exc_type	_resourcemem_mbs/                                                  r7   extract_knowledge_v2r  :  s   J OX>>% ":D "66$$T*s166!B</@B/F H 
 ^^E"":D "66$$T*s166!B</@B/F H  $,Haqvv/@/@/GHHH?M		
 	$UG5X?UVdUeeij	

 4512"$j),EEJ  "-djj9M9M9O.PQKCPWL>Yfgh'+%	G !lm,G+G4).+F8#k:JK0[1A0B#FG ,) ##+(+G$'L%(%5%&!)!9!9!;
  KK34)*))+J !GJ*%(II
*%(AA
)$**Z5I5I5K*LMGHWVcdeJ )$**Z5I5I5K*LMGGG~UbcdJ#d-J
 \NaO	

 	 .N~N^^bc	
 ,-(>:H!-H K#L3~q+AABFG"L
 $a' 7~6FeL>Ygh;N;K4P$0 .!"!"'	
*  "$Z&/!S\:.a'
 w	B"I{ $K+
2JKMLY^klm^n^w^w}Q/88;CCCRH}KK?ac-00G )$?AbQ\]`an]oQoPppqr )+MdAi[(KKXYM?Z\]hilmzi{]{\||~  @C  DQ  @R  S  Sd  e !%(\J%>%Bz$QMs 22S9O9OQR5SSVXX  C $%&/(+-4YKqOop{|}p}o~  A  BM  NQ  R_  N`  B`  Aa  ab  ,c080J0J,4,B,B-5-D-D-5-D-D&.oo)31A  "0!>''3 /-/ , '
 !0" #K 01 4! ''3}+=='""=1%&")+&v **c-.@@* mJ	=A JJL Z'#g,6

1KKb**&&'''' )$:8;R;R:SSbckczczb{  |F  G !$W
!:Q!>: M3..X5K5KQ1OORTT  !#(:5
G,!(j:6M!NJ!jm&<&<#-a=#9#9"1#= HC#&;;s#3D"3;;/"4 $)00CHH:Rv1FG	H
 @M%**]";RV . i[-8IJ`Iaabc"9+Q}oWE  ( ##.(0(B(B$,$:$:%-%<%<%-%<%<&oo!+&5)9+! *>T%&:;/@Aow	Bt KKi	 
/0G0G/HH_`h`w`w_xx{| Z(%'$,$>$> ( 6 6!)!8!8!)!8!8"//'
	
 E Iz  6'9--)1&^
 ;yk1UVW1$"a'"g&8&GO':'#$'$4q$8($
 ++s3C/DD+&&'78$4M  gLL!WYeffg &*LL P K(
 WX`Waabcd..#m2DD.$')*2,/$,$>$>&)(*@*@!&D%E&)%*-"
 2mmulvvw/x4<4N4N080F0F191H1H191H1H*2//-7$ '& **c-.@@**= +76'r **c-.@@*F  i117T 	 	
	((()>)>?IIDPFLL6? 		))56& # 22h44a89!
 &5^4DC	{#S(0(B(B$,$:$:%-%<%<%-%<%<&oo1!+$ 	?s   ?e3	?e8 e=0e=+B9n %Ef?C#n $En 
m&Am!$Ah;:m!;	i$im!i$$Cm!5m&6m): n m)!m&&m)) n		n 
r*-r%Apr%	pr%pBr%%r*)r4   r   returndict)rA   
list[dict]r  list[ThreadV2])rJ   r   rK   r   r  zOptional[datetime])r4   r   r  r   )rh   r   r  r  )rp   r  )r   r   r   r   ru   r   r  r   )ri   r   r  r   )ri   r   r  r   )r   r   r  r   )r   r   r   r   r   r  r  None)r   r   r  r  )FNN)
r0   list[ChatMessage]r   r!  r   
str | Noner   r  r  r  )r4   r   r   z	list[int]r  r  )r4   r   r  r!  r  )rB   r  r   r  r  r  )rB   r  r  list[Stage1Result])r4   r   r  r   )r   )rP  r  rQ  r   rR  r   r  r  )rV  r   r_  r   rQ  r   r  Optional[dict])r2  r  r4   r   r_  r   rQ  r   rz  r   r  r  )
r4   r   r_  r   rQ  r   r=   r   r  r  )r   r   r  r  )r   r   rA   r  r  r  )Fr   Nrl  Nr   r   )r0   r  r   r!  rQ  r   r   r  r@  r   r   r  r  r   r  r   r  r  )E
__future__r   r1   r  rb   loggingr|   r^   rz   r   r   pathlibr   typingr   knowledge_extractorr   r	   r
   r   r   r   modelsr   	models_v2r   r   r   r   r   r   	getLoggerr   ry   r   __annotations__r   r   r&   r.   rI  r\  r8   rE   rL   rX   rn   r   r   r   r   r   r   r   r   r  r   rD  r>  rY  rO  r]  r  rJ  r  r^  r   r   r  r>   rY   r7   <module>r     s   "  	   	 	     B B  0 0 
		8	$i   # /g/^/ 9/ W	/
 P/ \/ L/ H/ B/ V/ I/ G/ O/ >/ 7/  G!/" B#/$ b%/ + 4 ]f]!YQNN0 ,  4 D"V
L4DN$]	T&  $!	PPP P 	P
 Pf6 !%YYY YB   FP &  	>XXX X 	X:4 4 4  4  	4 
 4  
4 x;
$^ &  	"a a a  a  	a 
 a R&I4 ! $nnn n 	n
 n n n n nrY   