
    MiS                        S SK r S SKJr  S SKrS SKJrJrJrJrJ	r	  S SK
r
S SKJr  S SKrS SKrS SKrS SKJr  S SKJr  S SKJr  S SKJr  S S	KJr  S
SKJr  S SKrS SKrS SKrS SKrS\l        S\S\ 4S jr! " S S5      r"g)    N)Path)DictListTupleOptionalAny)tqdm)	BM25Okapi)word_tokenize)	stopwords)WordNetLemmatizer)batch_completion   )AsyncLoggerF	file_pathreturnc                   ^ [         R                  " 5       nU R                  S5       m[        U4S jS5       H  nUR	                  U5        M     SSS5        UR                  5       $ ! , (       d  f       UR                  5       $ = f)z/Compute MD5 hash for the file's entire content.rbc                  &   > T R                  S5      $ )Ni   )read)fs   p/var/www/eduai.edurigo.com/storigo/production/storigo_env/lib/python3.13/site-packages/crawl4ai/legacy/llmtxt.py<lambda>$_compute_file_hash.<locals>.<lambda>   s    !&&,    r   N)hashlibmd5openiterupdate	hexdigest)r   hash_md5chunkr   s      @r   _compute_file_hashr$      sj    {{}H		.4EOOE" 5 
  
	 s   )A))
Bc                   b   \ rS rSr   S$S\S\\   S\S\SS4
S jjrS	\	\   SS4S
 jr
S\S\\\\   4   4S jrS\S\4S jrS\S\SS4S jrS\S\	\   4S jrS%S\4S jjrS%S&S jjr S'S\S\SS4S jjrS(S\	\   S\S\4S jjrS)S\S\S\4S jjrS\	\   S\S \	\   S\4S! jrS&S" jrS#rg)*AsyncLLMTextManager!   Ndocs_dirloggermax_concurrent_calls
batch_sizer   c                     Xl         X l        X0l        X@l        S U l        0 U l        / U l        U R                   S-  U l        g )Nzbm25_index.pkl)r(   r)   r*   r+   
bm25_indexdocument_maptokenized_factsbm25_index_file)selfr(   r)   r*   r+   s        r   __init__AsyncLLMTextManager.__init__"   sB     !$8!$,.*,#}}/??r   	doc_batchc           
      V  #    / nU H7  n [        USSS9 nUR                  UR                  5       5        SSS5        M9     SnU Vs/ s H  nU(       d  M  S	U S
U 3S./PM     nn [        SUSS9n	[        X5       GH&  u  p [        R                  " SU
R                  S   R                  R                  [        R                  5      nU(       d   U R                  R                  SU 35        Mu  [        R                   " SSUR#                  S5      5      R%                  5       nU(       aW  UR'                  S5      n[        USSS9 nUR)                  U5        SSS5        U R                  R+                  SU 35        GM  U R                  R                  SU 35        GM)     g! , (       d  f       GM  = f! [         aG  nU R                  R                  SU S[        U5       35        UR                  S5         SnAGM  SnAff = fs  snf ! , (       d  f       N= f! [         a6  nU R                  R                  SU S[        U5       35         SnAGM  SnAff = f! [         a1  nU R                  R                  S[        U5       35         SnAgSnAff = f7f)z(Process a batch of documents in parallelrutf-8encodingNError reading :  a  Given a documentation file, generate a list of atomic facts where each fact:
1. Represents a single piece of knowledge
2. Contains variations in terminology for the same concept
3. References relevant code patterns if they exist
4. Is written in a way that would match natural language queries

Each fact should follow this format:
<main_concept>: <fact_statement> | <related_terms> | <code_reference>

Example Facts:
browser_config: Configure headless mode and browser type for AsyncWebCrawler | headless, browser_type, chromium, firefox | BrowserConfig(browser_type="chromium", headless=True)
redis_connection: Redis client connection requires host and port configuration | redis setup, redis client, connection params | Redis(host='localhost', port=6379, db=0)
pandas_filtering: Filter DataFrame rows using boolean conditions | dataframe filter, query, boolean indexing | df[df['column'] > 5]

Wrap your response in <index>...</index> tags.
userz*

Generate index for this documentation:

)rolecontentz"anthropic/claude-3-5-sonnet-latest)modelmessages	logger_fnz<index>(.*?)</index>r   z(No <index>...</index> content found for z\n\s*\n
r   .q.mdwzCreated index file: z'No index content found in response for zError processing response for zError in batch completion: )r   appendr   	Exceptionr)   errorstrr   zipresearchchoicesmessager?   DOTALLwarningsubgroupstripwith_suffixwriteinfo)r1   r4   contentsr   r   epromptr?   messages_list	responsesresponseindex_content_matchindex_content
index_files                 r   _process_document_batch+AsyncLLMTextManager._process_document_batch2   sz    "I$)S7;qOOAFFH- <; #2 $	
 $""()WX_W`a $ 	 	
(	F(:&I (+9'@#*,))/ ((+33;;		+'
 /++FykR !$&FF"D*=*C*CA*F%eg " %%.%:%:7%C
!*cGDGGM2 E((+?
|)LM++Ei[Q- (AW <; $!!N9+RAx"HI##$,	
J ED ! KK%%82c!fXN 
  	FKK ;CF8DEE	Fs   J)F> F+F>
J)HH&J))I+ A,H(2I+ 4AH(H&H(I+ H(%I+ *J)+
F;	5F>8J);F>>
H;H
J)
HJ)
H%	!H((
I(2*I#I+ #I((I+ +
J&5'J!J)!J&&J)linec                     SU;  a  gUR                  S5       Vs/ s H  o"R                  5       PM     nn[        U5      S:w  a  SS[        U5       34$ US   nSU;  a  gg	s  snf )
N|)FzMissing separator '|'   FzExpected 3 parts, got r   :)Fz!Missing ':' in concept definition)TN)splitrS   len)r1   rb   ppartsconcept_parts        r   _validate_fact_line'AsyncLLMTextManager._validate_fact_line   sj    d?1$(JJsO4OqO4u:?23u:,???Qxl"= 5s   A"	fact_filec           	      >   UR                  S5      n[        U5      nUR                  5       (       ab   [        US5       n[        R
                  " U5      nSSS5        WR                  S5      U:X  a  U$ U R                  R                  SU S35        0 US.$ ! , (       d  f       NI= f! [        R                   a"    U R                  R                  SU S35         NK[         a4  nU R                  R                  S	U S
[        U5       35         SnANSnAff = f)z}
Load token cache from .q.tokens if present and matching file hash.
Otherwise return a new structure with updated file-hash.
	.q.tokensr6   Ncontent_hashzHash changed for z, reindex needed.zCorrupt token cache for z, rebuilding.zError reading cache for r;   factsrq   )rT   r$   existsr   jsonloadgetr)   rV   JSONDecodeErrorrP   rG   rI   )r1   rn   
cache_filecurrent_hashr   cacherX   s          r   _load_or_create_token_cache/AsyncLLMTextManager._load_or_create_token_cache   s   
 **;7
))4V*c*a IIaLE + 99^,< L  #4YK?P!QR \:: +* '' Y##&>yk$WX V##&>ykCPQF8$TUUVs:   B* BB* 5B* 
B'#B* *3D	D(*DDr{   c                     UR                  S5      n[        U5      US'   [        US5       n[        R                  " X$5        S S S 5        g ! , (       d  f       g = f)Nrp   rq   rE   )rT   r$   r   ru   dump)r1   rn   r{   ry   r   s        r   _save_token_cache%AsyncLLMTextManager._save_token_cache   sE    **;7
 29 =n*c"aIIe #""s   A
Atextc           	      B   SU;   a/  UR                  S5       Vs/ s H  o"R                  5       PM     snOU/n[        R                  " SSUS   5      US'   [	        5       n[        [        R                  " S5      5      1 Sk-
  n/ nU H  nSU;   a.  SU;   a(  [        R                  " S	U5      nUR                  U5        [        UR                  5       5      n	UR                  U	 V
s/ s H  n
X;  d  M
  UR                  U
5      PM     sn
5        M     U$ s  snf s  sn
f )
Nrd   z^(.*?):z\1r   english>   howwhywhatwhenwherewhich()z.[\w_]+(?=\()|[\w_]+(?==[\'"]{1}[\w_]+[\'"]{1}))rg   rS   rK   rQ   r   setr   wordsfindallextendr   lower	lemmatize)r1   r   xrj   
lemmatizer
stop_wordstokenspartcode_tokensr   tokens              r   preprocess_text#AsyncLLMTextManager.preprocess_text   s   8;tDJJsO4OqO4$66*eU1X6a&(
34 8
 

 Dd{sd{ jjEt k*!$**,/EMM "'!&. 0J((/!&   ? 50s   D(	D
5D
c                 Z   U(       d  [         R                  R                  U R                  5      (       ae  U R                  R                  S5        [        U R                  S5       n[        R                  " U5      nSSS5        WS   U l	        US   U l
        gg! , (       d  f       N$= f)zG
Load existing BM25 index from disk, if present and clear_cache=False.
z&Loading existing BM25 index from disk.r   Nr/   r-   TF)ospathrt   r0   r)   rV   r   picklerv   r/   r-   )r1   clear_cacher   datas       r   maybe_load_bm25_index)AsyncLLMTextManager.maybe_load_bm25_index   s     rww~~d.B.BCCKKEFd**D1Q{{1~ 2#'(9#:D "<0DO 21s   'B
B*c                    U(       aT  U R                   R                  S5        U R                  R                  5       (       a  U R                  R	                  5         [
        R                  " 5       nU R                   R                  S5        [        R                  " U R                  5       Vs/ s H)  o3R                  S5      (       d  M  U R                  U-  PM+     nn/ n/ n/ n/ nU H  n	U	R                  S5      n
U(       d  U
R                  5       (       d  UR                  U	5        MC  U R                  U	5      n[        US   5      S:X  d  UR                  S5      [!        U	5      :w  a  UR                  U	5        M  US   R#                  5        H8  u  pUR                  U5        UR                  US   5        XR$                  U'   M:     M     U(       Gd  U(       d  U R'                  S	S
9(       a  U R                   R                  S5        gU R                   R                  S5        U(       a  U R                   R                  S[        U5       S35        [)        U5      U l        XPl        [/        U R                  S5       n[0        R2                  " U R*                  U R,                  S.U5        SSS5        gU R                   R5                  S5        gU R                   R                  [        U5       S35        / n/ n[7        [        U5      SS9 nU GH  n0 [!        U5      S.n [/        USSS9 nUR9                  5       R;                  5       nUR=                  S5       Vs/ s H*  nUR;                  5       (       d  M  UR;                  5       PM,     nnSSS5        W H  nU R?                  U5      u  nnU(       d  UR                  UUU45        M4  U RA                  U5      nU[B        RB                  " 5       S.US   U'   UR                  U5        UR                  U5        UU R$                  U'   M     U RE                  UU5        URG                  5       RH                  S-  S-  nU R                   RK                  SURL                   SUS S 35        URU                  S"5        GM     SSS5        U(       a\  U R                   R5                  S#[        U5       S$35        U H.  u  nnnU R                   R5                  U SU S%USS&  S'35        M0     X^-   nXo-   nU R                   R                  S[        U5       S(35        [)        U5      U l        UU l        [/        U R                  S5       n[0        R2                  " U R*                  U R,                  S.U5        SSS5        URG                  5       RH                  S-  S-  nU R                   R                  S)US S 35        gs  snf ! , (       d  f       g= fs  snf ! , (       d  f       GNy= f! [N         a5  nU R                   RQ                  S!U S[S        U5       35         SnAGNSnAff = f! , (       d  f       GN= f! , (       d  f       N= f)*z
Checks for new or modified .q.md files by comparing file-hash.
If none need reindexing and clear_cache is False, loads existing index if available.
Otherwise, reindexes only changed/new files and merges or creates a new index.
z0Clearing cache and rebuilding full search index.z/Checking which .q.md files need (re)indexing...rD   rp   rs   r   rq   r   Fr   z<No new/changed .q.md files found. Using existing BM25 index.Nz9No existing BM25 index found. Building from cached facts.zBuilding BM25 index with z cached facts.wb)r-   r/   z+No facts found at all. Index remains empty.z( file(s) need reindexing. Parsing now...zIndexing changed files)totaldescrr   r6   r7   r8   rC   )r   addedi   zMemory usage after r;   z.2fMBzError processing r   zFound z invalid fact lines:z
 in line: 2   z...z total facts (old + new).z*Search index updated. Final memory usage: )+r)   rV   r0   rt   unlinkpsutilProcessr   listdirr(   endswithrT   rF   r|   rh   rw   r$   itemsr.   r   r
   r-   r/   r   r   r   rP   r	   r   rS   rg   rl   r   timer   memory_inforssdebugnamerG   rH   rI   r    )r1   r   processr   q_filesexisting_factsexisting_tokensinvalid_linesneedSetqftoken_cache_filer{   rb   
cache_data	new_facts
new_tokens	file_pbarfilefresh_cachef_objr?   llinesis_validrH   r   	mem_usagerX   	all_facts
all_tokens	final_mems                                  r   build_search_index&AsyncLLMTextManager.build_search_index   s    KKOP##**,,$$++-.."JK (*zz$--'@
'@!JJwDWDMMA'@ 	 

 %'+- B!~~k: "2"9"9";";r" 44R8E5>"a'599,#B',( r" ).g(<(<(>$D"))$/#**:h+?@.0%%d+ )?# , {))e)<  R    O "KK$$3C4G3HW '0&@DO+9(d22D9Q.2oo373G3G  :  KK''(UV 	CL>)QRS 	
G+CD	(*<Nt<TULdC':e"'**,"4"4"64;MM$4G U4Gq17794G U ; !&*.*B*B4*H%')00$e1DE$!%!5!5d!;&,%)YY[6G,T2 "((."))&126))$/ !&  **4= ' 3 3 5 9 9D @4 GIKK%%-dii[9S/L   #E   EJ KK&]);(<<P QR%2!dE##tfBugZSb	{#$NO &3 #.	$1
 	'I'77PQ	
 $J/( $&&-KK"&//'+';';  . '')--4t;	EiPS_TVWXU
h :9 & !V ;:6 ! LKK%%(9$r#a&&JKKLA EDl .-s   .W8W8	/W=Y'1X%<2X.X
X
X!DX%"Y' /Y9=
XX
X"X%%
Y$/*YY'Y$$Y''
Y69
Zforce_generate_factsclear_bm25_cachec           	        ^#    U R                   R                  S5        [        R                  " U R                  5       V^s/ s HF  mTR                  S5      (       d  M  [        U4S jS 5       5      (       a  M7  U R                  T-  PMH     nnU(       dP  U Vs/ s HC  nU R                  UR                  R                  SS5      -  R                  5       (       a  MA  UPME     nnU(       d  U R                   R                  S5        O[        S[        U5      U R                  5       Hu  nXEXPR                  -    nU R                   R                  SXPR                  -  S	-    S
[        U5      U R                  -  S	-    35        U R                  U5      I Sh  vN   Mw     U R                   R                  S5        U R                  US9  gs  snf s  snf  N=7f)z
Generate index files for all documents in parallel batches

Args:
    force_generate_facts (bool): If True, regenerate indexes even if they exist
    clear_bm25_cache (bool): If True, clear existing BM25 index cache
z2Starting index generation for documentation files..mdc              3   F   >#    U  H  nTR                  U5      v   M     g 7fN)r   ).0r   r   s     r   	<genexpr>;AsyncLLMTextManager.generate_index_files.<locals>.<genexpr>  s     ,XDWqQZZ]]DWs   !)rD   .xs.mdrD   z4All index files exist. Use force=True to regenerate.r   zProcessing batch r   /Nz:Index generation complete, building/updating search index.r   )r)   rV   r   r   r(   r   anyr   replacert   rangerh   r+   r`   r   )r1   r   r   r   md_filesibatchs      `   r   generate_index_files(AsyncLLMTextManager.generate_index_files}  s     	MN ZZ.
.zz%  ),,XDW,X)X DMMA. 	 
 $ "!Aug(FFNNP !   KKST 1c(mT__= Q%89  '??(:Q(>'?q#h-QUQ`Q`B`deAe@fg 225999 > 	UV,<=5
 :s=   ?GG G<GGA G G&B5GG>Gsectionsmodec                   ^ [         R                   " [        U R                  S-  5      5      [         R                   " [        U R                  S-  5      5      -   nU Vs1 s HU  n[        U5      R                  R                  S5      (       a  M.  [        U5      R                  R                  S5      S   iMW     nnU(       a/  U V^s1 s H!  m[        U4S jU 5       5      (       d  M  TiM#     nn/ n[        US S9 H  mUS	:X  aW  U R                  T S
3-  nU R                  T S3-  n	UR                  [        UR                  5       (       a  UOU	5      5        M`  UR                  [        U R                  T S3-  5      5        M     / n
U HW  n [        USSS9 n[        U5      R                  nU
R                  S SU SS SUR                  5        35        S S S 5        MY     U
(       a  SR!                  U
5      $ S$ s  snf s  snf ! , (       d  f       M  = f! [         a5  nU R                  R                  SU S[        U5       35         S nAM  S nAff = f)Nz	[0-9]*.mdz[0-9]*.xs.mdrD   .r   c              3   d   >#    U  H%  oR                  5       TR                  5       ;   v   M'     g 7fr   )r   )r   sectiondocs     r   r   /AsyncLLMTextManager.generate.<locals>.<genexpr>  s      NX'}}#))+5Xs   -0c                     U R                  S5      S   R                  5       (       a  [        U R                  S5      S   5      $ S$ )N_r   i?B )rg   isdigitintr   s    r   r   .AsyncLLMTextManager.generate.<locals>.<lambda>  s8    !''#,q/2I2I2K2K#aggcl1o.WQWWr   )key	condensedr   r   r6   r7   r8   ####################z
# rC   z

r:   r;   

---

r<   )globrI   r(   r   r   r   rg   r   sortedrF   rt   r   r   rG   r)   rH   join)r1   r   r   	all_filesr   	base_docsr   filesxs_fileregular_filer?   r   fnamerX   s         `       r   generateAsyncLLMTextManager.generate  s   IIc$--+"=>?$))./C
 
	 
7<<((1 'DGLLs#A& 	 
  %$CNXNN $   W
C {"--SE.8#}}#c{:SGNN,<,<,OPSC5!<=>
 DE$g6! JOOENNfXT%6($qvvxj#QR 76  /6}!!'*=2=K
. 76  E!!N4&3q6("CDDEsO   -H+H	H+HH#!A H!H#
H 	H# H##
I"-*II"querytop_kc                    U R                   (       d  gU R                  U5      nU R                   R                  U5      n[        R                  " U5      n[        R
                  " U5      nUSU-  -   nU R                  UUUS9n[        UR                  5       S SS9S U n	/ n
U	 H  u  p[        U5      R                  SS5      n[        R                  R                  U R                  U-  5      (       d  MS  [        U R                  U-  S	S
S9 nUR!                  S5      S   nSSU 3SSUR#                  5       /nU
R%                  SR'                  U5      5        S S S 5        M     SR'                  U
5      $ ! , (       d  f       M  = f)Nz;No search index available. Call build_search_index() first.g      ?)
doc_scoresscore_thresholdquery_tokensc                 B    U S   S   S-  U S   S   S-  -   U S   S   -   $ )Nr   code_match_scoreg       @match_countg      ?total_score r   s    r   r   ,AsyncLLMTextManager.search.<locals>.<lambda>  s;    !'(3.A$}%+,A$}%&r   T)r   reverserD   r   r6   r7   r8   r   r   z# r<   rC   r   )r-   r   
get_scoresnpmeanstd_aggregate_search_scoresr   r   rI   r   r   r   rt   r(   r   rg   r   rF   r   )r1   r   r   r  r  
mean_score	std_scorer  	file_dataranked_filesresultsr   r   main_docr   only_file_namer?   s                    r   rL   AsyncLLMTextManager.search  sm   P++E2__//=
WWZ(
FF:&	$y(8911!+% 2 
	 OO
 
 5 #GD4y((%8Hww~~dmmh677$--(2C'Ja%-^^C%8%<N'2n-=)>"affhWGNN499W#56 KJ $ !!'** KJs   AE::
F		r  r  r  c                 "   0 n[        U5       H  u  pVXb::  a  M  U R                  U   nU R                  U   nX;  a	  SSS/ S.XH'   SU;   a  UR                  S5      OU/n	Sn
[	        U	5      S:X  aP  U	S   R                  5       nU R                  U5      n[	        [        U5      [        U5      -  5      [	        U5      -  n
XH   S==   U-  ss'   XH   S==   S-  ss'   [        XH   S	   U
5      XH   S	'   XH   S
   R                  U5        M     U$ )Nr   )r  r  r  matched_factsrd   re      r  r  r   r  r  )
	enumerater/   r.   rg   rh   rS   r   r   maxrF   )r1   r  r  r  r  idxscorefactr   
componentsr  code_refr   s                r   r  ,AsyncLLMTextManager._aggregate_search_scores  sA    	#J/JC''',D))$/I)#$#$()%'	(	$ -04KCdVJ :!#%a=..0"228<#&s<'83{;K'K#Ls P $   /58/ /14/7:$%78:J8I !34  188>= 0@ r   c                 "    U R                  SS9  g)z&Convenience method for a full rebuild.Tr   N)r   )r1   s    r   refresh_index!AsyncLLMTextManager.refresh_index   s    D1r   )r+   r-   r0   r(   r.   r)   r*   r/   )N   re   )F)r   N)FF)extended)r(  )__name__
__module____qualname____firstlineno__r   r   r   r   r2   r   r`   rI   r   boolrl   r   r|   r   r   r   r   r   r   rL   floatr  r&  __static_attributes__r	  r   r   r&   r&   !   sx    )-$%@@ %@ "	@
 @ 
@ PFtDz PFd PFd dHSM6I0J ;T ;d ;2 4       C  DI  D$ ZYz LQ&>$(&>DH&>	&>P,>c ,># ,>s ,>\$+C $+ $+C $+L%u+%8=%MQRUY%	%N2r   r&   )#r   pathlibr   rK   typingr   r   r   r   r   ru   r	   r   r   numpyr  	rank_bm25r
   nltk.tokenizer   nltk.corpusr   	nltk.stemr   litellmr   async_loggerr   r   r   r   set_verboserI   r$   r&   r	  r   r   <module>r;     sh    	  	 3 3       ' ! ' $ %      $  3  A2 A2r   