
    Mi#                       S SK JrJr  S SKrS SKrS SKJrJrJrJ	r	J
r
JrJr  S SKJrJr  S SKrS SKrS SKJrJr  SSKJrJrJrJrJr  SSKJrJrJrJrJ r J!r!  SS	K"7  SS
K"J#r#J$r$J%r%J&r&J'r'J(r(J)r)  SS	K*7  SSK*J+r+  SS	K,7  SSK,J-r-J.r.J/r/J0r0  SSK1J2r2J3r3  S SK4J5r5  S SK6r7S SK8r8S SK9J:r:  S SK;J<r<J=r=  S\>S\>4S jr?SJS\>S\@S\>4S jjrA " S S\5      rB " S S\B5      rC " S S\B5      rD " S S\B5      rE0 S\>_S \@_S!\F_S"\G_S#\H_S$\I_S%\J_S&\K_S'\L_S(\M_S)\N_S*\O_S+\P_S,\Q_S-\R_S.\S_S/\T_\U\V\W\X\Y\ZS0S1S\[\\S2.Er]S3\>S4\QS\4S5 jr^ " S6 S7\B5      r_ " S8 S9\_5      r` " S: S;\_5      ra " S< S=\_5      rb " S> S?\_5      rc \V" \d\Z" S@5      5       V s0 s H  o SA;  d  M
  U SB\e" U 5      SC 3_M     sn rf\8R                  " SD5      rh\8R                  " SE5      riSF\\>\>4   S\\>\>4   4SG jrj " SH SI\B5      rkgs  sn f )K    )ABCabstractmethodN)AnyListDictOptionalTuplePatternUnion)ThreadPoolExecutoras_completed)IntFlagauto   )PROMPT_EXTRACT_BLOCKS&PROMPT_EXTRACT_BLOCKS_WITH_INSTRUCTION&PROMPT_EXTRACT_SCHEMA_WITH_INSTRUCTIONJSON_SCHEMA_BUILDER_XPATHPROMPT_EXTRACT_INFERRED_SCHEMA)DEFAULT_PROVIDERDEFAULT_PROVIDER_API_KEYCHUNK_TOKEN_THRESHOLDOVERLAP_RATEWORD_TOKEN_RATEHTML_EXAMPLE_DELIMITER)*)sanitize_htmlescape_json_stringperform_completion_with_backoffextract_xml_datasplit_and_parse_json_objectssanitize_input_encodemerge_chunks)
TokenUsage)
get_deviceload_HF_embedding_modelload_text_multilabel_classifiercalculate_batch_size)	LLMConfigcreate_llm_config)partialBeautifulSoup)htmletreetextreturnc                     U R                  5       n [        R                  " SSU [        R                  S9R                  5       $ )zEStrip markdown code fences (e.g. ```json ... ```) from LLM responses.z^```(?:[a-zA-Z0-9_-]+)?\s*|```$ )flags)stripresub	MULTILINE)r0   s    v/var/www/eduai.edurigo.com/storigo/production/storigo_env/lib/python3.13/site-packages/crawl4ai/extraction_strategy.py_strip_markdown_fencesr:   3   s2    ::<D66*BBLLeg    html_content	max_depthc                    ^^^  [         R                  " U 5      nUR                  S5      nU(       a  US   OUn/ mUUU4S jmT" US5        SR	                  TSS 5      $ ! [         a     gf = f)zReturn a compact tag outline of the HTML body up to a given depth.

Used in schema validation feedback when baseSelector matches 0 elements,
so the LLM can see what top-level tags actually exist.
r3   z//bodyr   c                   > UT:  d  [        U R                  [        5      (       d  g U R                  SS5      R	                  5       nU(       a  SSR                  U5      -   OSnU R                  S5      (       a  SU R                  S5       3OSnTR                  SU-  SU R                   U U S3-   5        U  H  nT" XQS	-   5        M     g )
Nclassr3   .id#z  <>r   )
isinstancetagstrgetsplitjoinappend)	eldepthclassescls_strid_strchild_walklinesr=   s	         r9   rS   '_get_top_level_structure.<locals>._walkI   s    9Jrvvs$;$;&&"%++--4#))"')vvd||1RVVD\N#TE\axxy$BBCE%# r;   
N<   )r.   
fromstring	ExceptionxpathrK   )r<   r=   treebodyrootrS   rT   s    `   @@r9   _get_top_level_structurer^   ;   sq    |, ::hD47DE$ 
$N99U3BZ  #  s   A! !
A.-A.c            
           \ rS rSrSrSS\4S jjr\S\S\S\\	\\
4      4S j5       rS\S	\\   S\\	\\
4      4S
 jrS\S	\\   S\\	\\
4      4S jrSrg)ExtractionStrategyW   z4
Abstract base class for all extraction strategies.
input_formatc                     Xl         SU l        U R                  R                  U l        UR                  SS5      U l        g)z
Initialize the extraction strategy.

Args:
    input_format: Content format to use for extraction.
                 Options: "markdown" (default), "html", "fit_markdown"
    **kwargs: Additional keyword arguments
z<|DEL|>verboseFN)rb   DEL	__class____name__namerI   rd   )selfrb   kwargss      r9   __init__ExtractionStrategy.__init__\   s4     )NN++	zz)U3r;   urlr.   r1   c                     g)z
Extract meaningful blocks or chunks from the given HTML.

:param url: The URL of the webpage.
:param html: The HTML content of the webpage.
:return: A list of extracted blocks or chunks.
N ri   rm   r.   qrj   s        r9   extractExtractionStrategy.extractj   s     	r;   sectionsc           	         / n[        5        nU Vs/ s H!  nUR                  " U R                  X40 UD6PM#     nn[        U5       H"  n	UR	                  U	R                  5       5        M$     SSS5        U$ s  snf ! , (       d  f       U$ = f)z
Process sections of text in parallel by default.

:param url: The URL of the webpage.
:param sections: List of sections (strings) to process.
:return: A list of processed JSON blocks.
N)r   submitrr   r   extendresult)
ri   rm   rt   rq   rj   extracted_contentexecutorsectionfuturesfutures
             r9   runExtractionStrategy.runu   s     !X  ('G cEfE'   'w/!((9 0 " !  "! ! s   A<(A73A<7A<<
Bc                 h   #    SSK nUR                  " U R                  X/UQ70 UD6I Sh  vN $  N7f)aC  
Async version: Process sections of text in parallel using asyncio.

Default implementation runs the sync version in a thread pool.
Subclasses can override this for true async processing.

:param url: The URL of the webpage.
:param sections: List of sections (strings) to process.
:return: A list of processed JSON blocks.
r   N)asyncio	to_threadr~   )ri   rm   rt   rq   rj   r   s         r9   arunExtractionStrategy.arun   s1      	&&txxMMfMMMMs   )202)re   rb   rh   rd   N)markdown)rg   
__module____qualname____firstlineno____doc__rH   rk   r   r   r   r   rr   r~   r   __static_attributes__ro   r;   r9   r`   r`   W   s    4S 4 3 c Dc3h<P  !s !d3i !$tCQTH~BV !$Nc NT#Y NdSVX[S[nI] Nr;   r`   c            	       j    \ rS rSrSrS\S\S\\\\4      4S jr	S\S\\   S\\\\4      4S jr
S	rg
)NoExtractionStrategy   z}
A strategy that does not extract any meaningful content from the HTML. It simply returns the entire HTML as a single block.
rm   r.   r1   c                     SUS./$ )z:
Extract meaningful blocks or chunks from the given HTML.
r   )indexcontentro   rp   s        r9   rr   NoExtractionStrategy.extract   s     -..r;   rt   c                 V    [        U5       VVs/ s H  u  pVU/ US.PM     snn$ s  snnf )Nr   tagsr   )	enumerate)ri   rm   rt   rq   rj   ir{   s          r9   r~   NoExtractionStrategy.run   s7     (1
1
 81
 	
 
s   %ro   N)rg   r   r   r   r   rH   r   r   r   rr   r~   r   ro   r;   r9   r   r      sZ    /3 /c /Dc3h<P /
s 
d3i 
$tCQTH~BV 
r;   r   c            
          ^  \ rS rSrSr       SU 4S jjr SS\\   S\S\S\\   4S jjr	 SS	\\   4S
 jjr
SS	\\   4S jjrS\\\\   4   S\\\\   4   4S jrS\S\S\\\\4      4S jrS\S\\   S\\\\4      4S jrSrU =r$ )CosineStrategy   af  
Extract meaningful blocks or chunks from the given HTML using cosine similarity.

How it works:
1. Pre-filter documents using embeddings and semantic_filter.
2. Perform clustering using cosine similarity.
3. Organize texts by their cluster labels, retaining order.
4. Filter clusters by word count.
5. Extract meaningful blocks or chunks from the filtered clusters.

Attributes:
    semantic_filter (str): A keyword filter for document filtering.
    word_count_threshold (int): Minimum number of words per cluster.
    max_dist (float): The maximum cophenetic distance on the dendrogram to form clusters.
    linkage_method (str): The linkage method for hierarchical clustering.
    top_k (int): Number of top categories to extract.
    model_name (str): The name of the sentence-transformers model.
    sim_threshold (float): The similarity threshold for clustering.
c                   > [         TU ]  " S0 UD6  SSKn	Xl        X l        X0l        X@l        XPl        Xpl        [        R                  " 5       U l
        UR                  SS5      U l        U	R                  " / 5      U l        SU l        [!        5       U l        [%        U R"                  5      U l        U R                  (       a#  [)        SU R"                  R*                   S35        [-        U5      u  U l        U l        U R0                  R3                  U R"                  5        U R0                  R5                  5         SU l        U	R                  " / 5      U l        U R                  (       a#  [)        S	U R"                  R*                   S35        [7        5       u  U l        n
U R                  (       a?  [)        S
U S3[;        [        R                  " 5       U R                  -
  5      -   S-   5        gg)a  
Initialize the strategy with clustering parameters.

Args:
    semantic_filter (str): A keyword filter for document filtering.
    word_count_threshold (int): Minimum number of words per cluster.
    max_dist (float): The maximum cophenetic distance on the dendrogram to form clusters.
    linkage_method (str): The linkage method for hierarchical clustering.
    top_k (int): Number of top categories to extract.
r   Nrd   Fdirectz#[LOG] Loading Extraction Model for z device.batchz([LOG] Loading Multilabel Classifier for z[LOG] Model loaded z, models/reuters, took  secondsro   )superrk   numpysemantic_filterword_count_thresholdmax_distlinkage_methodtop_ksim_thresholdtimetimerrI   rd   arraybuffer_embeddingsget_embedding_methodr%   devicer(   default_batch_sizeprinttyper&   	tokenizermodeltoevalr'   nlprH   )ri   r   r   r   r   r   
model_namer   rj   np_rf   s              r9   rk   CosineStrategy.__init__   s{   * 	"6".$8! ,
*YY[
zz)U3!#"$,! l #7t{{"C<<78H8H7IRS &=Z%H"


dkk"

$+!!#" <<<T[[=M=M<NhWX57! <<%j\1HIdiikDJJ./0 r;   	documentsr   
at_least_kr1   c                    U(       d  U$ [        U5      U:  a  [        S[        U5      S-  5      nSSKJn  U R	                  U/5      S   nU R	                  U5      nU" U/U5      R                  5       n[        X5       VV	s/ s H  u  pXR                  :  d  M  X4PM     n
nn	[        U
5      U:  ab  [        X5       VV	s/ s H  u  pXR                  :  d  M  X4PM     nnn	UR                  S SS9  U
R                  USU[        U
5      -
   5        U
 VVs/ s H  u  pUPM	     n
nnU
SU $ s  sn	nf s  sn	nf s  snnf )	az  
Filter and sort documents based on the cosine similarity of their embeddings with the semantic_filter embedding.

Args:
    documents (List[str]): A list of document texts.
    semantic_filter (str): A keyword filter for document filtering.
    at_least_k (int): The minimum number of documents to return.

Returns:
    List[str]: A list of filtered and sorted document texts.
r      r   )cosine_similarityc                     U S   $ )Nr   ro   )xs    r9   <lambda><CosineStrategy.filter_documents_embeddings.<locals>.<lambda>H  s    adr;   T)keyreverseN)
lenmaxsklearn.metrics.pairwiser   get_embeddingsflattenzipr   sortrw   )ri   r   r   r   r   query_embeddingdocument_embeddingssimilaritiesdocsimfiltered_docsremaining_docsr   s                r9   filter_documents_embeddings*CosineStrategy.filter_documents_embeddings  sg    y>J&QI! 34J> --.?@C #11)< )2

') 	  	8
8((( SJ8 	 
 }
* !$I < <HC+++ 
 <  
 NDA  0Q*s=?Q2Q!RS ,99==9[j))'
 :s   D2D2D8D8D>	sentencesc           	         U R                   R                  S;   Ga  SSKnUc  U R                  n/ n[	        S[        U5      U5       H  nXXb-    nU R                  USSSS9nUR                  5        V	V
s0 s H   u  pXR                  U R                   5      _M"     nn	n
UR                  5          U R                  " S
0 UD6nSSS5        WR                  R                  SS9R                  5       R                  5       nUR                  U5        M     [         R"                  " U5      U l        U R$                  $ U R                   R                  S	:X  aq  Uc  U R                  n/ n[	        S[        U5      U5       H+  nXXb-    nU R                  U5      nUR                  U5        M-     [         R"                  " U5      U l        U R$                  $ s  sn
n	f ! , (       d  f       GN= f)z
Get BERT embeddings for a list of sentences.

Args:
    sentences (List[str]): A list of text chunks (sentences).

Returns:
    NumPy array of embeddings.
)cpugpucudampsr   NTpt)padding
truncationreturn_tensorsr   )dimr   ro   )r   r   torchr   ranger   r   itemsr   no_gradr   last_hidden_statemeanr   r   rL   r   vstackr   )ri   r   
batch_sizebypass_bufferr   all_embeddingsr   batch_sentencesencoded_inputr   tensormodel_output
embeddingss                r9   r   CosineStrategy.get_embeddingsP  s    ;;<< !!44
N1c)nj9"+"? $#TdSW !/ ! DQCVCVCX!CXKCC4;;//CX  !
 ]]_#'::#>#>L % *;;@@Q@GKKMSSU
%%j1 :" &(YY~%>D" %%% [[&!!44
N1c)nj9"+"?!ZZ8
%%j1 :
 &(YY~%>D"%%%3!
 %_s   5'G/G
G'	c                     SSK JnJn  SSKJn  [
        R
                  " 5       U l        U R                  USS9nU" US5      nU" X`R                  S9nU" XpR                  SS	9nU$ )
z
Perform hierarchical clustering on sentences and return cluster labels.

Args:
    sentences (List[str]): A list of text chunks (sentences).

Returns:
    NumPy array of cluster labels.
r   )linkagefcluster)pdistT)r   cosine)methoddistance)	criterion)
scipy.cluster.hierarchyr   r   scipy.spatial.distancer   r   r   r   r   r   )	ri   r   r   r   r   r   distance_matrixlinkedlabelss	            r9   hierarchical_clustering&CosineStrategy.hierarchical_clustering  s_     	>0YY[
(($(G
  
H51D1DE&--:Fr;   clustersc                     0 nUR                  5        HD  u  p4SR                  U5      n[        UR                  5       5      nX`R                  :  d  M@  XBU'   MF     U$ )z
Filter clusters to remove those with a word count below the threshold.

Args:
    clusters (Dict[int, List[str]]): Dictionary of clusters.

Returns:
    Dict[int, List[str]]: Filtered dictionary of clusters.
 )r   rK   r   rJ   r   )ri   r   filtered_clusters
cluster_idtexts	full_text
word_counts          r9   filter_clusters_by_word_count,CosineStrategy.filter_clusters_by_word_count  sZ     !)!1JIY__./J 66605*- "2 ! r;   rm   r.   c           	      6   [         R                   " 5       nUR                  " U R                  5      n[        U5      S:X  aF  UR                  " S5       Vs/ s H)  owR	                  5       (       d  M  UR	                  5       PM+     nnU R                  X`R                  5      nU(       d  / $ U R                  U5      n[         R                   " 5       n0 n	[        U5       H(  u  pU	R                  U/ 5      R                  Xj   5        M*     U R                  U	5      n[        U5       Vs/ s H#  n[        U5      / SR                  X   5      S.PM%     nnU R                  (       a  [!        SU R"                   35        U R"                  R$                  S;   a<  U R'                  U Vs/ s H  oS   PM	     sn5      n[)        X5       H	  u  pXS'   M     U R                  (       a&  [!        S	[         R                   " 5       U-
  S
 S35        U$ s  snf s  snf s  snf )z
Extract clusters from HTML content using hierarchical clustering.

Args:
    url (str): The URL of the webpage.
    html (str): The HTML content of the webpage.

Returns:
    List[Dict[str, Any]]: A list of processed JSON blocks.
r   

r   r   u   [LOG] 🚀 Assign tags using )r   r   r   r   r   r   u"   [LOG] 🚀 Categorization done in z.2fr   )r   rJ   re   r   r5   r   r   r   r   
setdefaultrL   r  sortedintrK   rd   r   r   r   r   r   )ri   rm   r.   rq   rj   ttext_chunkschunkr   r   r   labelr  idxcluster_listclusters                   r9   rr   CosineStrategy.extract  s    IIKjj*{q 6:jj6HZ6HUKKM=5;;=6HKZ 66--
 I --k: IIK%f-LEr*11+2DE . !>>xH
 /0
0 #hsxx@Q@V7WX0 	 

 <<1$++?@;;<<XXNgy1NOF"%l";"' #<& <<6tyy{Qs6K8TUs [0
 Os   H1H(*H&Hrt   c                 Z    U R                   " XR                  R                  U5      40 UD6$ )z
Process sections using hierarchical clustering.

Args:
    url (str): The URL of the webpage.
    sections (List[str]): List of sections (strings) to process.

Returns:
)rr   re   rK   )ri   rm   rt   rq   rj   s        r9   r~   CosineStrategy.run  s%     ||Cx!8CFCCr;   )r   r   r   r   r   r   r   r   r   r   r   r   r   rd   r   )N
   g?ward   z&sentence-transformers/all-MiniLM-L6-v2g333333?)   )NFN)rg   r   r   r   r   rk   r   rH   r  r   r   r   r   r  r   rr   r~   r   __classcell__rf   s   @r9   r   r      s   , ;Tn MO6*c6*586*FI6*	c6*r DI5&c5&nc 2!S$s)^,!	c49n	!2I3 Ic IDc3h<P IVDs Dd3i D$tCQTH~BV D Dr;   r   c                   \  ^  \ rS rSrSrSSSSS.rSSSS\\\S	S
SS\	SSS4SSS\
S\S\
S\
S\\
   S\
S\
4U 4S jjjrU 4S jrS\
S\S\
S\\\
\4      4S jrS\\
   4S jrS\
S\\
   S\\\
\4      4S jrS\
S\S\
S\\\
\4      4S jrS\
S\\
   S\\\
\4      4S  jrS#S! jrS"rU =r$ )$LLMExtractionStrategyi  ah  
A strategy that uses an LLM to extract meaningful content from the HTML.

Attributes:
    llm_config: The LLM configuration object.
    instruction: The instruction to use for the LLM model.
    schema: Pydantic model schema for structured data.
    extraction_type: "block" or "schema".
    chunk_token_threshold: Maximum tokens per chunk.
    overlap_rate: Overlap between chunks.
    word_token_rate: Word to token conversion rate.
    apply_chunking: Whether to apply chunking.
    verbose: Whether to print verbose output.
    usages: List of individual token usages.
    total_usage: Accumulated token usage.
1Instead, use llm_config=LLMConfig(provider="...")2Instead, use llm_config=LlMConfig(api_token="...")z1Instead, use llm_config=LLMConfig(base_url="..."))provider	api_tokenbase_urlapi_baseNblockTr   F
llm_configr)   instructionschemarb   r"  r#  r$  r%  c                   > [         TU ]  " SSU	0UD6  Xl        U R                  (       d4  [        [        [
        R                  R                  [        5      S9U l        X l	        X@l
        X0l        U(       a  SU l
        Xl        U=(       d    [        U l        X`l        Xpl        Xl        UR                  S0 5      U l        U R"                  (       d  SU l        Xl        / U l        [+        5       U l        Xl        Xl        Xl        Xl        g)ae  
Initialize the strategy with clustering parameters.

Args:
    llm_config: The LLM configuration object.
    instruction: The instruction to use for the LLM model.
    schema: Pydantic model schema for structured data.
    extraction_type: "block" or "schema".
    chunk_token_threshold: Maximum tokens per chunk.
    overlap_rate: Overlap between chunks.
    word_token_rate: Word to token conversion rate.
    apply_chunking: Whether to apply chunking.
    input_format: Content format to use for extraction.
                    Options: "markdown" (default), "html", "fit_markdown"
    force_json_response: Whether to force a JSON response from the LLM.
    verbose: Whether to print verbose output.

    # Deprecated arguments, will be removed very soon
    provider: The provider to use for extraction. It follows the format <provider_name>/<model_name>, e.g., "ollama/llama3.3".
    api_token: The API token for the provider.
    base_url: The base URL for the API request.
    api_base: The base URL for the API request.
    extra_args: Additional arguments for the API request, such as temperature, max_tokens, etc.
rb   r"  r#  r)  
extra_argsg    eANro   )r   rk   r'  r*   r   osenvironrI   r   r(  extract_typer)  force_json_responser   chunk_token_thresholdoverlap_rateword_token_rateapply_chunkingr,  rd   usagesr$   total_usager"  r#  r$  r%  )ri   r'  r(  r)  extraction_typer1  r2  r3  r4  rb   r0  rd   r"  r#  r$  r%  rj   rf   s                    r9   rk   LLMExtractionStrategy.__init__-  s    X 	>|>v>$/)**..)ABDO '+ (D#6 %:%S>S"(., **\26""),D&%< "  r;   c                    > [         R                  " U R                  5      nUR                  nXR                  ;   a/  X$U   R
                  La  [        SU SU R                  U    35      e[        TU ]!  X5        g)zHandle attribute setting.	Setting '' is deprecated. N)	inspect	signaturerk   
parameters_UNWANTED_PROPSdefaultAttributeErrorr   __setattr__)ri   rh   valuesig
all_paramsrf   s        r9   rB  !LLMExtractionStrategy.__setattr__w  sq     .^^
'''ED9I9Q9Q,Q 9TF2CDDXDXY]D^C_!`aaD(r;   rm   ixr.   r1   c                 
   U R                   (       a  [        SU SU 35        U[        [        U5      5      S.n[        nU R
                  (       a  U R
                  US'   [        nU R                  S:X  a9  U R                  (       a(  [        R                  " U R                  SS9US'   [        nU R                  S:X  a  U R                  (       d  [        nU H  nUR                  S	U-   S
-   XF   5      nM      [        U R                  R                   UU R                  R"                  U R                  R$                  U R&                  U R(                  U R                  R*                  U R                  R,                  U R                  R.                  S9	n[1        UR2                  R4                  UR2                  R6                  UR2                  R8                  UR2                  R:                  (       a   UR2                  R:                  R<                  O0 UR2                  R>                  (       a   UR2                  R>                  R<                  O0 S9nU R@                  RC                  U5        U RD                  =R4                  UR4                  -  sl        U RD                  =R6                  UR6                  -  sl        U RD                  =R8                  UR8                  -  sl         URF                  S   RH                  RJ                  n	Sn
U	(       d(  [M        URF                  S   SS5      nSSS/SU S3S./n
OU R&                  (       a  [        RN                  " [Q        U	5      5      n
[S        U
[T        5      (       a_  [W        U
5      S:X  aL  [S        [Y        U
R[                  5       5      S   [X        5      (       a  [Y        U
R[                  5       5      S   n
OBU
/n
O>[S        U
[X        5      (       a  U
n
O&[]        S/U	5      S   n
[        RN                  " U
5      n
U
 H  nSUS'   M
     U R                   (       a  [        S[W        U
5      SUSU5        U
$ ! [^         a\    URF                  S   RH                  RJ                  =(       d    Sn[a        U5      u  pUn
U(       a  U
RC                  SSS/US.5         Nf = f! [^         a;  nU R                   (       a  [        SU 35        USS/[c        U5      S./s SnA$ SnAff = f)a  
Extract meaningful blocks or chunks from the given HTML using an LLM.

How it works:
1. Construct a prompt with variables.
2. Make a request to the LLM using the prompt.
3. Parse the response and extract blocks or chunks.

Args:
    url: The URL of the webpage.
    ix: Index of the block.
    html: The HTML content of the webpage.

Returns:
    A list of extracted blocks or chunks.
[LOG] Call LLM for  - block index: URLHTMLREQUESTr)  r   indentSCHEMA{}r$  json_responser,  
base_delaymax_attemptsexponential_factorcompletion_tokensprompt_tokenstotal_tokenscompletion_tokens_detailsprompt_tokens_detailsr   Nfinish_reasonunknownTerror(LLM returned no content (finish_reason: )r   ra  r   r   r   blocksFr3   [LOG] Extractedblocks from URL:block index:[LOG] Error in LLM extraction: )2rd   r   r   r   r   r(  r   r/  r)  jsondumpsr   r   replacer   r'  r"  r#  r$  r0  r,  backoff_base_delaybackoff_max_attemptsbackoff_exponential_factorr$   usagerZ  r[  r\  r]  __dict__r^  r5  rL   r6  choicesmessager   getattrloadsr:   rF   dictr   listvaluesr    rY   r!   rH   )ri   rm   rG  r.   variable_valuesprompt_with_variablesvariableresponserp  r   re  r_  r&  raw_contentparsedunparsedes                    r9   rr   LLMExtractionStrategy.extract  s6   " <<'u,<RDAB &}T':;

 !6)-)9)9OI&$J!(T[[(,

4;;q(IOH%$J!($B!'H$9$A$Ah$o&?%! (
X	6((%))11"66????==!__AA#'??#M#M
H "*.."B"B&nn::%^^88>>;; +3..*R*R*[*[>>77 '/nn&J&J&S&S
E KKu% ..%2I2II.**e.A.AA*))U-?-??)""**1-55==$+H,<,<Q,?R[$\M()D7),TUbTccd*eg hF--!ZZ(>w(GHF!&$//v;!+
4;PQR;SUY0Z0Z%)&--/%:1%=F '-XF#FD11!' .xj'B8LF!ZZ/F#E%*E'N $ ||%K&" M%  &..q199AAGR#?#L MM"#dWIRZ[&  	||7s;<  !$I"1v	 		sE   :G2S -EQ 1+S A#S S SS 
T0T TTc                 2    [        UUUU R                  S9nU$ )zK
Merge documents into sections based on chunk_token_threshold and overlap.
)docstarget_sizeoverlapword_token_ratio)r#   r3  )ri   r   r1  r  rt   s        r9   _mergeLLMExtractionStrategy._merge  s'     !.!11	
 r;   rt   c                    U R                  UU R                  [        U R                  U R                  -  5      S9n/ nU R                  R
                  R                  S5      (       ac  [        U5       HR  u  pV[        U R                  U5      nUR                  U" U[        U5      5      5        [        R                  " S5        MT     U$ [        SS9 n[        U R                  U5      n[        U5       VVs/ s H   u  pVUR                  Xu[        U5      5      PM"     n	nn[!        U	5       H#  n
 UR                  U
R#                  5       5        M%     SSS5        U$ s  snnf ! [$         aJ  nU R&                  (       a  [)        SU 35        UR+                  SSS	/[-        U5      S
.5         SnAM  SnAff = f! , (       d  f       U$ = f)z
Process sections sequentially with a delay for rate limiting issues, specifically for LLMExtractionStrategy.

Args:
    url: The URL of the webpage.
    sections: List of sections (strings) to process.

Returns:
    A list of extracted blocks or chunks.
r  zgroq/g      ?   max_workerszError in thread execution: r   Tra  rd  N)r  r1  r  r2  r'  r"  
startswithr   r+   rr   rw   r"   r   sleepr   rv   r   rx   rY   rd   r   rL   rH   )ri   rm   rt   merged_sectionsry   rG  r{   extract_funcrz   r|   r}   r  s               r9   r~   LLMExtractionStrategy.run  s    ++&&22T5F5FFG & 

 ??##..w77(9&t||S9!(( %:7%CD 

3  :F ! / $2h&t||S9 (1'A'A OOL6KG6TU'A  
 +73F)00A 4 3. ! + % <<!$?s"CD)00)*)-)0	+.q6	 	 32. ! sH   %F<9'E F<2E%F<F<%
F9/?F4.F<4F99F<<
Gc                 B
  #    SSK Jn  U R                  (       a  [        SU SU 35        U[	        [        U5      5      S.n[        nU R                  (       a  U R                  US'   [        nU R                  S:X  a9  U R                  (       a(  [        R                  " U R                  SS	9US
'   [        nU R                  S:X  a  U R                  (       d  [        nU H  nUR                  SU-   S-   XW   5      nM      U" U R                   R"                  UU R                   R$                  U R                   R&                  U R(                  U R*                  U R                   R,                  U R                   R.                  U R                   R0                  S9	I Sh  vN n[3        UR4                  R6                  UR4                  R8                  UR4                  R:                  UR4                  R<                  (       a   UR4                  R<                  R>                  O0 UR4                  R@                  (       a   UR4                  R@                  R>                  O0 S9n	U RB                  RE                  U	5        U RF                  =R6                  U	R6                  -  sl        U RF                  =R8                  U	R8                  -  sl        U RF                  =R:                  U	R:                  -  sl         URH                  S   RJ                  RL                  n
SnU
(       d(  [O        URH                  S   SS5      nSSS/SU S3S./nOU R(                  (       a  [        RP                  " [S        U
5      5      n[U        U[V        5      (       a_  [Y        U5      S:X  aL  [U        [[        UR]                  5       5      S   [Z        5      (       a  [[        UR]                  5       5      S   nOBU/nO>[U        U[Z        5      (       a  UnO&[_        S/U
5      S   n[        RP                  " U5      nU H  nSUS'   M
     U R                  (       a  [        S[Y        U5      SUSU5        U$  GN! [`         a]    URH                  S   RJ                  RL                  =(       d    Sn[c        U5      u  nnUnU(       a  URE                  SSS/US.5         Nf = f! [`         a;  nU R                  (       a  [        SU 35        USS/[e        U5      S./s SnA$ SnAff = f7f)a  
Async version: Extract meaningful blocks or chunks from the given HTML using an LLM.

How it works:
1. Construct a prompt with variables.
2. Make an async request to the LLM using the prompt.
3. Parse the response and extract blocks or chunks.

Args:
    url: The URL of the webpage.
    ix: Index of the block.
    html: The HTML content of the webpage.

Returns:
    A list of extracted blocks or chunks.
r    aperform_completion_with_backoffrI  rJ  rK  rN  r)  r   rO  rQ  rR  rS  rT  NrY  r   r_  r`  Tra  rb  rc  rd  re  Fr3   rf  rg  rh  ri  )3utilsr  rd   r   r   r   r   r(  r   r/  r)  rj  rk  r   r   rl  r'  r"  r#  r$  r0  r,  rm  rn  ro  r$   rp  rZ  r[  r\  r]  rq  r^  r5  rL   r6  rr  rs  r   rt  ru  r:   rF   rv  r   rw  rx  r    rY   r!   rH   )ri   rm   rG  r.   r  ry  rz  r{  r|  rp  r   re  r_  r&  r}  r~  r  r  s                     r9   aextractLLMExtractionStrategy.aextractL  sE    " 	<<<'u,<RDAB &}T':;

 !6)-)9)9OI&$J!(T[[(,

4;;q(IOH%$J!($B!'H$9$A$Ah$o&?%! (
S	=((%))11"66????==!__AA#'??#M#M
 
H "*.."B"B&nn::%^^88>>;; +3..*R*R*[*[>>77 '/nn&J&J&S&S
E KKu% ..%2I2II.**e.A.AA*))U-?-??)"**1-55==$+H,<,<Q,?R[$\M()D7),TUbTccd*eg hF--!ZZ(>w(GHF!&$//v;!+
4;PQR;SUY0Z0Z%)&--/%:1%=F&,XF#FD11!'-xj'B8LF!ZZ/F#E%*E'N $ ||%K&" MO
j  &..q199AAGR#?#L MM"#dWIRZ[&  
	||7s;<  !$I"1v	 
	sp   C?TBS Q* ES :EQ- >+S )T*S -A$SS SS 
T!0TTTTTc                 $  #    SSK nU R                  UU R                  [        U R                  U R                  -  5      S9n/ n[        U5       VVs/ s H   u  pgU R                  X[        U5      5      PM"     nnnUR                  " USS06I Sh  vN n	U	 Hj  n
[        U
[        5      (       aA  U R                  (       a  [        SU
 35        UR                  SSS/[        U
5      S.5        MY  UR                  U
5        Ml     U$ s  snnf  N|7f)	z
Async version: Process sections with true parallelism using asyncio.gather.

Args:
    url: The URL of the webpage.
    sections: List of sections (strings) to process.

Returns:
    A list of extracted blocks or chunks.
r   Nr  return_exceptionsTzError in async extraction: ra  rd  )r   r  r1  r  r2  r   r  r"   gatherrF   rY   rd   r   rL   rH   rw   )ri   rm   rt   r   r  ry   rG  r{   tasksresultsrx   s              r9   r   LLMExtractionStrategy.arun  s     	++&&22T5F5FFG & 
 
  )9
9 MM##8#AB9 	 
  FFF F&),,<<7x@A!((!"!%!(	#&v;	 "((0  ! 1
 Gs   AD'D9DDA=Dc                 F   [        S5        [        SS SSS 35        [        S5        [        SS SU R                  R                  S	 35        [        S
S SU R                  R                  S	 35        [        SS SU R                  R                  S	 35        [        S5        [        SS SSS SS
S SSS 35        [        S5        [        U R                  S5       H=  u  p[        US SUR                  S	 SUR                  S	 SUR                  S	 35        M?     g)zHPrint a detailed token usage report showing total and per-request usage.z
=== Token Usage Summary ===Typez<15r   Countz>12z------------------------------
Completionz>12,PromptTotalz
=== Usage History ===z	Request #z<10z0------------------------------------------------r   N)r   r6  rZ  r[  r\  r   r5  )ri   r   rp  s      r9   
show_usage LLMExtractionStrategy.show_usage  s(   -.Agc]+,hc"!D$4$4$F$Ft#LMN#a 0 0 > >tDEFQt//<<TBCD'(S!<"4Ahs^1WSMRSh!$++q1HAS'52248%:M:Md9SSTUZUgUghlTmn 2r;   )r%  r#  r4  r$  r1  r,  r/  r0  r(  r'  r2  r"  r)  r6  r5  rd   r3  )r1   N)rg   r   r   r   r   r?  r   r   r   r   rH   r   r   rk   rB  r  r   r   rr   r  r~   r  r   r  r   r  r  s   @r9   r  r    s   " MNLL	
O #'3!'&!(#'#G!G! G! 	G! G! G! C=G!  !G!" #G! G!T	)C3 CC Cs CtDcN7K CJ
49 
7!s 7!d3i 7!Dc3h4H 7!r# 3 c d4S>>R B.!c .!T#Y .!4S#X;O .!` r;   r  rH   r  floatboolr   roundabsminr   sumr  reversedrw  rv  tuplesetr   TF)r   mapfilteranyallr   TrueFalseNonerF   r   
expression
local_varsc                     [         R                  " U SS9n[         R                  " U5       GHv  n[        U[         R                  [         R                  45      (       a  [        S5      e[        U[         R                  5      (       a9  UR                  R                  S5      (       a  [        SUR                   S35      e[        U[         R                  5      (       d  M  UR                  n[        U[         R                  5      (       a9  UR                  R                  S5      (       a  [        S	UR                   S
35      e[        U[         R                  5      (       d  GM=  UR                  R                  S5      (       d  GM`  [        S	UR                   S
35      e   S[        0n[!        [#        USS5      Xa5      $ ! [         a  n[        SU 35      eSnAff = f)a  
Evaluate a computed field expression safely using AST validation.

Allows simple transforms (math, string methods, attribute access on data)
while blocking dangerous operations (__import__, dunder access, etc.).

Args:
    expression: The Python expression string to evaluate.
    local_vars: The local variables (extracted item fields) available to the expression.

Returns:
    The result of evaluating the expression.

Raises:
    ValueError: If the expression contains disallowed constructs.
r   )modezInvalid expression syntax: Nz0Import statements are not allowed in expressionsr   z$Access to private/dunder attribute 'z' is not allowedz	Calling 'z' is not allowed in expressions__builtins__z<expression>)astparseSyntaxError
ValueErrorwalkrF   Import
ImportFrom	Attributeattrr  CallfuncNamerB   _SAFE_EVAL_BUILTINSr   compile)r  r  r[   r  noder  safe_globalss          r9   _safe_eval_expressionr     s   "<yy&1 dSZZ899OPP dCMM**tyy/C/CC/H/H6tyykAQR 
 dCHH%%99D$))dgg.@.@.E.E y(GH  $..4993G3G3L3L 		{*IJ ' . #$78Lnf5|PP7  <6qc:;;<s   G 
G$GG$c                   4  ^  \ rS rSrSrSrS\\\4   4U 4S jjr	S\S\S\
\\\4      4S	 jr\S\4S
 j5       r\S\4S j5       r\S\4S j5       rS rS rS rS rS rS rS\S\
\   S\
\\\4      4S jr\S\4S j5       r\S\4S j5       r\S\4S j5       r\S\4S j5       r\  S=S\S\S\S\\
\      S\4
S  jj5       r\S!\S\S"\S#\ S\4
S$ j5       r!\S>S%\S&\S\S'S(S\\   4
S) jj5       r"\S*\S\
\   4S+ j5       r#S,S-S..r$\S>S/\S\S%\S0\S\4
S1 jj5       r%\SSSS\&" 5       SSSS2S3S4S/\S\S%\S0\S4S5S6\S7\S\'\\
\   4   S8\ S9\S'S(S\4S: jj5       r(\           S?S/\S\S%\S0\S4S5S6\S7\S\'\\
\   4   S8\ S9\S'S(S\4S; jj5       r)S<r*U =r+$ )@JsonElementExtractionStrategyiQ  a  
    Abstract base class for extracting structured JSON from HTML content.

    How it works:
    1. Parses HTML content using the `_parse_html` method.
    2. Uses a schema to define base selectors, fields, and transformations.
    3. Extracts data hierarchically, supporting nested fields and lists.
    4. Handles computed fields with expressions or functions.

    Attributes:
        DEL (str): Delimiter used to combine HTML sections. Defaults to '
'.
        schema (Dict[str, Any]): The schema defining the extraction rules.
        verbose (bool): Enables verbose logging for debugging purposes.

    Methods:
        extract(url, html_content, *q, **kwargs): Extracts structured data from HTML content.
        _extract_item(element, fields): Extracts fields from a single element.
        _extract_single_field(element, field): Extracts a single field based on its type.
        _apply_transform(value, transform): Applies a transformation to a value.
        _compute_field(item, field): Computes a field value using an expression or function.
        run(url, sections, *q, **kwargs): Combines HTML sections and runs the extraction strategy.

    Abstract Methods:
        _parse_html(html_content): Parses raw HTML into a structured format (e.g., BeautifulSoup or lxml).
        _get_base_elements(parsed_html, selector): Retrieves base elements using a selector.
        _get_elements(element, selector): Retrieves child elements using a selector.
        _get_element_text(element): Extracts text content from an element.
        _get_element_html(element): Extracts raw HTML from an element.
        _get_element_attribute(element, attribute): Extracts an attribute's value from an element.
    rV   r)  c                 `   > [         TU ]  " S0 UD6  Xl        UR                  SS5      U l        g)z
Initialize the JSON element extraction strategy with a schema.

Args:
    schema (Dict[str, Any]): The schema defining the extraction rules.
rd   FNro   )r   rk   r)  rI   rd   ri   r)  rj   rf   s      r9   rk   &JsonElementExtractionStrategy.__init__s  s,     	"6"zz)U3r;   rm   r<   r1   c                    U R                  U5      nU R                  XPR                  S   5      n/ nU H  n0 n	SU R                  ;   a3  U R                  S    H   n
U R                  X5      nUc  M  XU
S   '   M"     U R	                  XR                  S   5      nU	R                  U5        U	(       d  M  UR                  U	5        M     U$ )a?  
Extract structured data from HTML content.

How it works:
1. Parses the HTML content using the `_parse_html` method.
2. Identifies base elements using the schema's base selector.
3. Extracts fields from each base element using `_extract_item`.

Args:
    url (str): The URL of the page being processed.
    html_content (str): The raw HTML content to parse and extract.
    *q: Additional positional arguments.
    **kwargs: Additional keyword arguments for custom extraction.

Returns:
    List[Dict[str, Any]]: A list of extracted items, each represented as a dictionary.
baseSelector
baseFieldsrh   fields)_parse_html_get_base_elementsr)  _extract_single_field_extract_itemupdaterL   )ri   rm   r<   rq   rj   parsed_htmlbase_elementsr  elementitemfieldrC  
field_datas                r9   rr   %JsonElementExtractionStrategy.extract~  s    * &&|4//^4
 $GDt{{*![[6E 66wFE(.3U6]+ 7 ++G[[5JKJKK
#tt$ %  r;   c                     g)z*Parse HTML content into appropriate formatNro   ri   r<   s     r9   r  )JsonElementExtractionStrategy._parse_html       	r;   selectorc                     g)(Get all base elements using the selectorNro   ri   r  r  s      r9   r  0JsonElementExtractionStrategy._get_base_elements  r  r;   c                     g)z%Get child elements using the selectorNro   ri   r  r  s      r9   _get_elements+JsonElementExtractionStrategy._get_elements  r  r;   c           	          SU;   a(  U R                  XS   5      nUc  UR                  S5      $ US   S:X  a?  U R                  XS   5      nU(       a  US   OS nU(       a  U R                  XBS   5      $ 0 $ US   S:X  a8  U R                  XS   5      nU Vs/ s H  o`R	                  XbS   5      PM     sn$ US   S	:X  a8  U R                  XS   5      nU Vs/ s H  o`R                  XbS   5      PM     sn$ U R                  X5      $ s  snf s  snf ! [         aI  nU R                  (       a  [        S
US    S[        U5       35        UR                  S5      s S nA$ S nAff = f)Nsourcer@  r   nestedr  r   r  rw  nested_listzError extracting field rh   : )
_resolve_sourcerI   r  r  _extract_list_itemr  rY   rd   r   rH   )ri   r  r  nested_elementsnested_elementelementsrM   r  s           r9   _extract_field,JsonElementExtractionStrategy._extract_field  sy   	(5 ..whH? 99Y//V}("&"4"4WJ>O"P7F!3D & &&~XG  V}&--gZ7HIOWXx//(ODxXXV}---gZ7HIJRS(B**2X?(SS--g== Y T  	(||/fbQIJ99Y''	(sY   -D AD 6D 8!D D6D 9!D D7D :D 
D 
E(>E#E(#E(c                    SU;   a2  U R                  XS   5      nU(       d  UR                  S5      $ US   nOUnUS   n[        U[        5      (       d  U/nUnU H  nUS:X  a  U R	                  U5      nOUS:X  a  U R                  XRS   5      nOUS:X  a  U R                  U5      nOUS:X  a  UR                  S	5      nU(       a~  [        U[        5      (       d  U R	                  U5      n[        U[        5      (       aA  [        R                  " Xu5      nU(       a!  UR                  UR                  S
S5      5      OSnOSnUb  M    O   SU;   a  U R                  XRS   5      nUb  U$ UR                  S5      $ )a  
Extract a single field based on its type.

How it works:
1. Selects the target element using the field's selector.
2. Extracts the field value based on its type (e.g., text, attribute, regex).
3. Applies transformations if defined in the schema.

Args:
    element: The base element to extract the field from.
    field (Dict[str, Any]): The field definition in the schema.

Returns:
    Any: The extracted field value.
r  r@  r   r   r0   	attributer.   regexpatterngroupr   N	transform)r  rI   rF   rw  _get_element_text_get_element_attribute_get_element_htmlrH   r6   searchr  _apply_transform)	ri   r  r  selectedtype_pipelinerC  stepr  matchs	            r9   r  3JsonElementExtractionStrategy._extract_single_field  sk   " ))'3DEHyy++{HHf-..*OM!Dv~..u5$33E;MN..u5))I.%eS11 $ 6 6u =!%-- "		' 9FKEIIgq,A BQU $}' "* %))%{1CDE)uCuyy/CCr;   c                 V    0 nU H   nU R                  X5      nUc  M  XSUS   '   M"     U$ )Nrh   )r  ri   r  r  r  r  rC  s         r9   r  0JsonElementExtractionStrategy._extract_list_item  s:    E..w>E &+U6]#  r;   c                     0 nU H;  nUS   S:X  a  U R                  X45      nOU R                  X5      nUc  M4  XSUS   '   M=     U$ )a  
Extracts fields from a given element.

How it works:
1. Iterates through the fields defined in the schema.
2. Handles computed, single, and nested field types.
3. Updates the item dictionary with extracted field values.

Args:
    element: The base element to extract fields from.
    fields (List[Dict[str, Any]]): The list of fields to extract.

Returns:
    Dict[str, Any]: A dictionary representing the extracted item.
r   computedrh   )_compute_fieldr  r  s         r9   r  +JsonElementExtractionStrategy._extract_item  sX    " EV}
*++D8++G; &+U6]#  r;   c                     US:X  a  UR                  5       $ US:X  a  UR                  5       $ US:X  a  UR                  5       $ U$ )aU  
Apply a transformation to a value.

How it works:
1. Checks the transformation type (e.g., `lowercase`, `strip`).
2. Applies the transformation to the value.
3. Returns the transformed value.

Args:
    value (str): The value to transform.
    transform (str): The type of transformation to apply.

Returns:
    str: The transformed value.
	lowercase	uppercaser5   )lowerupperr5   )ri   rC  r   s      r9   r  .JsonElementExtractionStrategy._apply_transform2  sE    " #;;= +%;;= '!;;= r;   c           	           SU;   a  [        US   U5      $ SU;   a  US   " U5      $ g ! [         aI  nU R                  (       a  [        SUS    S[	        U5       35        UR                  S5      s S nA$ S nAff = f)Nr  functionzError computing field rh   r  r@  )r  rY   rd   r   rH   rI   )ri   r  r  r  s       r9   r  ,JsonElementExtractionStrategy._compute_fieldK  s    	(u$,U<-@$GGu$Z(.. % 	(||.uV}oRAxHI99Y''	(s   ) ) 
A<>A71A<7A<rt   c                 ^    U R                   R                  U5      nU R                  " X40 UD6$ )a  
Run the extraction strategy on a combined HTML content.

How it works:
1. Combines multiple HTML sections using the `DEL` delimiter.
2. Calls the `extract` method with the combined HTML.

Args:
    url (str): The URL of the page being processed.
    sections (List[str]): A list of HTML sections.
    *q: Additional positional arguments.
    **kwargs: Additional keyword arguments for custom extraction.

Returns:
    List[Dict[str, Any]]: A list of extracted items.
)re   rK   rr   )ri   rm   rt   rq   rj   combined_htmls         r9   r~   !JsonElementExtractionStrategy.runV  s*    $ h/||C9&99r;   c                     g)zGet text content from elementNro   ri   r  s     r9   r  /JsonElementExtractionStrategy._get_element_textk  r  r;   c                     g)zGet HTML content from elementNro   r  s     r9   r  /JsonElementExtractionStrategy._get_element_htmlp  r  r;   r  c                     g)z Get attribute value from elementNro   ri   r  r  s      r9   r  4JsonElementExtractionStrategy._get_element_attributeu  r  r;   r  c                     g)a  Navigate to a sibling element relative to the base element.

Used when a field's data lives in a sibling of the base element
rather than a descendant. For example, Hacker News splits each
submission across two sibling <tr> rows.

Args:
    element: The current base element.
    source: A sibling selector string. Currently supports the
        ``"+ <selector>"`` syntax which navigates to the next
        sibling matching ``<selector>``.

Returns:
    The resolved sibling element, or ``None`` if not found.
Nro   )ri   r  r  s      r9   r  -JsonElementExtractionStrategy._resolve_sourcez  s    " 	r;   CSSNschema_typeexpected_fieldsc           
      2  ^ SSSSS/ / SSS.	n UR                  5       S:X  a  [        O[        nU" U S9nUR                  SUS9n UR                  U5      n	UR                  XS   5      n
[        U
5      US'   U
(       a  UR                  U
S   5      nUSS US'   US   S:X  a8  US	   R                  SU R                  SS5       S35        [        U5      US'   U$ U R                  S/ 5      nU Vs/ s H  oS   PM	     nn[        U5      US'   U H|  nU Vs/ s H  nUR                  U5      PM     nn[        S U 5       5      n[        S U 5       S5      nUb  [        U5      SS nUS   R                  UU[        U5      US.5        M~     [        S US    5       5      US'   US   S:  a  US   US   -  US'   US   S:X  a  US	   R                  S5        OPUS    Vs/ s H  nUS   S:X  d  M  US   PM     nnU(       a&  US	   R                  S S!R                  U5       35        U(       aj  U R                  S/ 5       Vs1 s H  oS   iM	     nnU Vs/ s H  oU;  d  M
  UPM     nnU(       a&  US	   R                  S"S!R                  U5       35        U(       aq  U R                  S/ 5       Vs1 s H  oS   iM	     nnUS    Vs1 s H  nUS   S:  d  M  US   iM     snmUS   S:  =(       a    [!        U4S# jU 5       5      US$'   U$ US   S:  =(       a    US   S:  US$'   U$ ! [         a#  nUS	   R                  S
U 35        Us SnA$ SnAff = f! [         a     GNf = fs  snf s  snf s  snf s  snf s  snf s  snf s  snf )%u7  Run the generated schema against HTML and return a diagnostic result.

Args:
    schema: The extraction schema to validate.
    html_content: The HTML to validate against.
    schema_type: "CSS" or "XPATH".
    expected_fields: When provided, enables strict mode — success
        requires ALL expected fields to be present and populated.
        When None, uses fuzzy mode (populated_fields > 0).

Returns a dict with keys: success, base_elements_found, total_fields,
populated_fields, field_coverage, field_details, issues,
sample_base_html, top_level_structure.
Fr   g        r3   )	successbase_elements_foundtotal_fieldspopulated_fieldsfield_coveragefield_detailsissuessample_base_htmltop_level_structurer(  )r)  )rm   r<   r2  zExtraction crashed: Nr  r-    r3  zbaseSelector 'z' matched 0 elementsr4  r  rh   r.  c              3   >   #    U  H  oc  M  US:w  d  M  Sv   M     g 7f)Nr3   r   ro   .0vs     r9   	<genexpr>AJsonElementExtractionStrategy._validate_schema.<locals>.<genexpr>  s     !QV!b!!V   	c              3   >   #    U  H  oc  M  US:w  d  M  Uv   M     g 7f)Nr3   ro   r7  s     r9   r:  r;    s     N&Qqa2gqq&r<  x   r1  )rh   populated_counttotal_countsample_valuec              3   :   #    U  H  oS    S:  d  M  Sv   M     g7f)r?  r   r   Nro   )r8  fds     r9   r:  r;    s!      )
/"6G3H13LAA/s   	r/  r0  u9   All fields returned None/empty — selectors likely wrongr?  zFields always empty: z, z%Expected fields missing from schema: c              3   ,   >#    U  H	  oT;   v   M     g 7fr  ro   )r8  fpopulated_namess     r9   r:  r;    s     Fo_,o   r,  )r  JsonCssExtractionStrategyJsonXPathExtractionStrategyrr   rY   rL   r  r  r   r  rI   r^   r  nextrH   rK   r  )r)  r<   r)  r*  rx   StrategyClassstrategyr   r  r~  r  sample
all_fieldsrE  field_namesfnamer  rx  r?  
sample_valrC  empty_fieldsschema_field_namesmissingrF  s                           @r9   _validate_schema.JsonElementExtractionStrategy._validate_schema  sW   , #$ !! "#%


	 $$&%/ *0 
 %F3H$$,$GE
	)),7F$77~@VWM,/,>F() !33M!4DE-3ET])* '(A-8## NB!? @@TU -E\,RF()M ZZ"-
*45*Qy*5!$[!1~ E278%$dhhuo%F8!!QV!QQON&NPTUJ% _Tc2
?#**#2"5z *	,  ! &) )
/)
 &
!" .!A%'-.@'AF>DZ'ZF#$ $%*8##K !11B'(A- 6
1  
 x ''+DIIl,C+DE
 5;ZZ"5M!N5MF)5M!N"1Q/Q>P5Pq/GQx '';DIIg<N;OP
 5;ZZ"5M!N5MF)5M!N%+O%<%<rCT@UXY@Y
6
%<O ,-1 GFoFF 9  ,-1Tf=O6PST6T 9 s  	8##&:1#$>?M	  		 6 90 "OQ "Osl   6L5 AM% 3M6M;'N 8	N N	N
,N
<NN$	N5
M"?MM"M"%
M32M3validation_resultattemptis_repeatedc                 b   U n/ nUR                  SU 35        US   S:X  aJ  UR                  SUR                  SS5       S35        US   (       a  UR                  S	US   -   S
-   5        OUR                  SUS    S35        US   (       a  UR                  SUS    SUS    S35        UR                  S5        UR                  S5        US    H8  nUS   =(       d    SnUR                  SUS    SUS    SUS    SU S3	5        M:     US   (       a1  UR                  S5        US    H  nUR                  SU 35        M     US   S:X  a$  US    (       a  UR                  S!US    -   S
-   5        U(       a  UR                  S"5        UR                  S#5        S$R                  U5      $ )%z=Build a structured feedback message from a validation result.u!   ## Schema Validation — Attempt r-  r   z**CRITICAL:** baseSelector `r  r3   z=` matched **0 elements**. The schema cannot extract anything.r4  zKHere is the top-level HTML structure so you can pick a valid selector:
```

```zbaseSelector matched **z** element(s).r1  z
**Field coverage:** r/  /r.  z fields have data
z| Field | Populated | Sample |z|-------|-----------|--------|rA  z	*(empty)*z| rh   z | r?  r@  z |r2  z
**Issues:**z- r3  ue   
Here is the innerHTML of the first base element — use it to find correct child selectors:
```html
zr
**WARNING:** You returned the exact same schema as before. You MUST change the selectors to fix the issues above.z@
Please fix the schema and return ONLY valid JSON, nothing else.rV   )rL   rI   rK   )	rW  r)  rX  rY  vrpartsrC  rM  issues	            r9   _build_feedback_message5JsonElementExtractionStrategy._build_feedback_message  s    8	BC #$)LL.vzz."/M.N ON O '(c./0 LL)"-B*C)DNS
 oLL(,>)?(@"^BTAUUhi LL9:LL9:)N+:{FC+<(='>a=@Q?RRUV\U]]_` * h<LL)Hr%\* &  !Q&2.@+ALLE'()  LLI
 	O	
 yyr;   queryhtml_snippetrp  r$   c                   #    SSK Jn  U(       a  SU S3OSnSU SU  SUS	S
  S3n U" UR                  USUR                  UR                  S9I S	h  vN nUb{  U=R
                  UR                  R
                  -  sl        U=R                  UR                  R                  -  sl        U=R                  UR                  R                  -  sl        UR                  S   R                  R                  n	U	(       a  U	R                  5       (       d  g	[        R                  " [        U	5      5      $  N! [          a     g	f = f7f)a  Infer a target JSON example from a query and HTML snippet via a quick LLM call.

Args:
    usage: Optional TokenUsage accumulator. If provided, token counts from
           this LLM call are added to it in-place.

Returns the parsed dict, or None if inference fails.
r   r  zURL: rV   r3   u  You are given a data extraction request and a snippet of HTML from a webpage.
Your job is to produce a single example JSON object representing ONE item that the user wants to extract.

Rules:
- Return ONLY a valid JSON object — one flat object, NOT wrapped in an array or outer key.
- The object represents a single repeated item (e.g., one product, one article, one row).
- Use clean snake_case field names matching the user's description.
- If the item has nested repeated sub-items, represent those as an array with one example inside.
- Fill values with realistic examples from the HTML so the meaning is clear.

Example — if the request is "extract product name, price, and reviews":
{"name": "Widget Pro", "price": "$29.99", "reviews": [{"author": "Jane", "text": "Great product"}]}

zExtraction request: z

HTML snippet:
```html
Nr5  z/
```

Return ONLY the JSON object for ONE item:T)r"  rz  rU  r#  r$  r   )r  r  r"  r#  r$  rZ  rp  r[  r\  rr  rs  r   r5   rj  ru  r:   rY   )
rb  rc  r'  rm   rp  r  url_linepromptr|  raws
             r9   _infer_target_json0JsonElementExtractionStrategy._infer_target_jsonW  s7     	<&)U3%r?r
v j"5' *''3ET':&; <88 	$	=#,,&,"$..#,, H  ''8>>+K+KK'##x~~'C'CC#""hnn&A&AA"""1%--55Cciikk::4S9::  		sG   %E+D7 D5CD7 ED7 4E5D7 7
EEEEtarget_jsonc                 4    [        U R                  5       5      $ )z9Extract top-level field names from a target JSON example.)rw  keys)rj  s    r9   _extract_expected_fields6JsonElementExtractionStrategy._extract_expected_fields  s     K$$&''r;   r   r!  r+  r.   target_json_examplec                 "   SSK Jn  US:X  a  UO[        nSU 3nSU  S3nU(       a  USU 3-  nU(       a	  USU S	3-  nU(       a  U(       d  US
-  nO'U(       d  U(       a  US-  nOU(       d  U(       d  US-  nUS-  nSR                  Xg/5      $ )z~
Build the prompt for schema generation. Shared by sync and async methods.

Returns:
    str: Combined system and user prompt
r   )JSON_SCHEMA_BUILDERr(  a#  You specialize in generating special JSON schemas for web scraping. This schema uses CSS or XPATH selectors to present a repetitive pattern in crawled HTML, such as a product in a product list or a search result item in a list of search results. We use this JSON schema to pass to a language model along with the HTML content to extract structured data from the HTML. The language model uses the JSON schema to extract data from the HTML and retrieve values for fields in the JSON schema, following the schema.

Generating this HTML manually is not feasible, so you need to generate the JSON schema using the HTML content. The HTML copied from the crawled website is provided below, which we believe contains the repetitive pattern.

# Schema main keys:
- name: This is the name of the schema.
- baseSelector: This is the CSS or XPATH selector that identifies the base element that contains all the repetitive patterns.
- baseFields: This is a list of fields that you extract from the base element itself.
- fields: This is a list of fields that you extract from the children of the base element. {name, selector, type} based on the type, you may have extra keys such as "attribute" when the type is "attribute".

# Extra Context:
In this context, the following items may or may not be present:
- Example of target JSON object: This is a sample of the final JSON object that we hope to extract from the HTML using the schema you are generating.
- Extra Instructions: This is optional instructions to consider when generating the schema provided by the user.
- Query or explanation of target/goal data item: This is a description of what data we are trying to extract from the HTML. This explanation means we're not sure about the rigid schema of the structures we want, so we leave it to you to use your expertise to create the best and most comprehensive structures aimed at maximizing data extraction from this page. You must ensure that you do not pick up nuances that may exist on a particular page. The focus should be on the data we are extracting, and it must be valid, safe, and robust based on the given HTML.

# What if there is no example of target JSON object and also no extra instructions or even no explanation of target/goal data item?
In this scenario, use your best judgment to generate the schema. You need to examine the content of the page and understand the data it provides. If the page contains repetitive data, such as lists of items, products, jobs, places, books, or movies, focus on one single item that repeats. If the page is a detailed page about one product or item, create a schema to extract the entire structured data. At this stage, you must think and decide for yourself. Try to maximize the number of fields that you can extract from the HTML.

# What are the instructions and details for this schema generation?
zJ
                HTML to analyze:
                ```html
                z%
                ```
                z4

## Query or explanation of target/goal data item:
z,

## Example of target JSON object:
```json
r[  a  IMPORTANT: To remind you, in this process, we are not providing a rigid example of the adjacent objects we seek. We rely on your understanding of the explanation provided in the above section. Make sure to grasp what we are looking for and, based on that, create the best schema..a  IMPORTANT: Please remember that in this process, we provided a proper example of a target JSON object. Make sure to adhere to the structure and create a schema that exactly fits this example. If you find that some elements on the page do not match completely, vote for the majority.zIMPORTANT: Since we neither have a query nor an example, it is crucial to rely solely on the HTML content provided. Leverage your expertise to determine the schema based on the repetitive patterns observed in the content.a  IMPORTANT:
        0/ Ensure your schema remains reliable by avoiding selectors that appear to generate dynamically and are not dependable. You want a reliable schema, as it consistently returns the same data even after many page reloads.
        1/ DO NOT USE use base64 kind of classes, they are temporary and not reliable.
        2/ Every selector must refer to only one unique element. You should ensure your selector points to a single element and is unique to the place that contains the information. You have to use available techniques based on CSS or XPATH requested schema to make sure your selector is unique and also not fragile, meaning if we reload the page now or in the future, the selector should remain reliable.
        3/ Do not use Regex as much as possible.

        Analyze the HTML and generate a JSON schema that follows the specified format. Only output valid JSON schema, nothing else.
        r	  )promptsrq  r   rK   )r.   r)  rb  ro  rq  prompt_templatesystem_contentuser_contents           r9   _build_schema_prompt2JsonElementExtractionStrategy._build_schema_prompt  s     	11<1E-Kd(  ),   UV[U\]]LNObNcchiiL,  {  {L.  }  }L2  @  @L  	 {{N9::r;   Tr  r'  r)   r"  r#  validatemax_refinementsc                    SSK n UR                  5       n[        R                  " SU UUUUUUUUU	U
S.UD6nUc  UR                  U5      $ SSKnUR                  R                  SS9 nUR                  UR
                  U5      nUR                  5       sSSS5        $ ! [         a    Sn Nf = f! , (       d  f       g= f)ae  
Generate extraction schema from HTML content or URL(s) (sync version).

Args:
    html (str, optional): The HTML content to analyze. If not provided, url must be set.
    schema_type (str): "CSS" or "XPATH". Defaults to "CSS".
    query (str, optional): Natural language description of what data to extract.
    target_json_example (str, optional): Example of desired JSON output.
    llm_config (LLMConfig): LLM configuration object.
    provider (str): Legacy Parameter. LLM provider to use.
    api_token (str): Legacy Parameter. API token for LLM provider.
    url (str or List[str], optional): URL(s) to fetch HTML from. If provided, html parameter is ignored.
        When multiple URLs are provided, HTMLs are fetched in parallel and concatenated.
    validate (bool): If True, validate the schema against the HTML and
        refine via LLM feedback loop. Defaults to False (zero overhead).
    max_refinements (int): Max refinement rounds when validate=True. Defaults to 3.
    usage (TokenUsage, optional): Token usage accumulator. If provided,
        token counts from all LLM calls (including inference and
        validation retries) are added to it in-place.
    **kwargs: Additional args passed to LLM processor.

Returns:
    dict: Generated schema following the JsonElementExtractionStrategy format.

Raises:
    ValueError: If neither html nor url is provided.
r   N)r.   r)  rb  ro  r'  r"  r#  rm   rx  ry  rp  r   r  ro   )r   get_running_loopRuntimeErrorr  agenerate_schemar~   concurrent.futuresr|   r   rv   rx   )r.   r)  rb  ro  r'  r"  r#  rm   rx  ry  rp  rj   r   loopcoro
concurrentrz   r}   s                     r9   generate_schema-JsonElementExtractionStrategy.generate_schema  s    T 		++-D -== 
# 3!+
 
 <;;t$$%##6616E!d;}} FE-  	D	, FEs   B ),B1B.-B.1
B?c                 j  #    SSK JnJn  U c2  Ub$  [        U[        5      (       a  [        U5      S:X  a  [        S5      e[        R                  R                  5        H$  u  p[        5       U   c  M  [        SU SU 35      e   Uc
  [        5       n/ nUGb
  SSKJn  SS	KJnJnJn  U" S
S
S
S9nU" UR&                  S9n[        U[(        5      (       a  U/OUnU" US9 ISh  vN n[        U5      S:X  a  UR+                  US   US9I Sh  vN nUR,                  (       d  [/        SUS    SUR0                   35      eUR2                  S:  a  [/        SUR2                   SUS    S35      eUR4                  n UR4                  /nGOUR7                  UUS9I Sh  vN n/ n[9        US5       H  u  nnUR,                  (       d%  [/        SUR:                   SUR0                   35      eUR2                  S:  a&  [/        SUR2                   SUR:                   S35      eUR=                  UR4                  5        U" UR4                  SSSS9n[>        R@                  " US9nUR=                  U SU 35        M     SRC                  U5      n SSS5      ISh  vN   OU /nUb  [        U[(        5      (       a	  U" U SSSS9n SnU(       a  U(       aE   [        U[(        5      (       a  [D        RF                  " U5      n OUn [        RI                  U 5      nO~U(       aw  Sn!Ub  [        U[(        5      (       a  UOUS   n![        RO                  X UU!U
S9I Sh  vN n"U"(       a1  [        RI                  U"5      nU(       d  [D        RP                  " U"SS9n[        RS                  XX#5      n#S U#S!./n$Sn%Sn&SU(       a  U	OS-   n'[U        U'5       GH  n( U" URV                  U#S
URX                  URZ                  U$US"9I Sh  vN n)U
b{  U
=R\                  U)R^                  R\                  -  sl.        U
=R`                  U)R^                  R`                  -  sl0        U
=Rb                  U)R^                  Rb                  -  sl1        U)Rd                  S   Rf                  Rh                  n*U*(       a  U*Rk                  5       (       d  [        S#5      e[D        RF                  " [m        U*5      5      n+U+n&U(       d  U+s  $ Sn-U H6  n.[        Ro                  U+U.UUS)9n/U-b  U/S*   U-S*   :  a  U/n-U/S+   (       d  M6    O   U-S+   (       a  U+s  $ U(U'S-
  :  a  U+s  $ [D        RP                  " U+S
S,9n0U0U%:H  n1U0n%[        Rq                  U-U+U(S-   U15      n2U$R=                  S%U*S!.5        U$R=                  S U2S!.5        GM     U&b  U&$ [/        S-5      e GN GN GN1 GN7! , ISh  vN  (       d  f       GNI= f! [D        RJ                  [L        4 a     GNuf = f GN GN! [D        RJ                   a_  n,U(       a	  U(U'S-
  :  a  [/        S$[)        U,5       35      eU$R=                  S%W*S!.5        U$R=                  S S&U, S'3S!.5         Sn,A,GM  Sn,A,f[.         a  n,[/        S([)        U,5       35      eSn,A,ff = f7f).a
  
Generate extraction schema from HTML content or URL(s) (async version).

Use this method when calling from async contexts (e.g., FastAPI) to avoid
issues with certain LLM providers (e.g., Gemini/Vertex AI) that require
async execution.

Args:
    html (str, optional): The HTML content to analyze. If not provided, url must be set.
    schema_type (str): "CSS" or "XPATH". Defaults to "CSS".
    query (str, optional): Natural language description of what data to extract.
    target_json_example (str, optional): Example of desired JSON output.
    llm_config (LLMConfig): LLM configuration object.
    provider (str): Legacy Parameter. LLM provider to use.
    api_token (str): Legacy Parameter. API token for LLM provider.
    url (str or List[str], optional): URL(s) to fetch HTML from. If provided, html parameter is ignored.
        When multiple URLs are provided, HTMLs are fetched in parallel and concatenated.
    validate (bool): If True, validate the schema against the HTML and
        refine via LLM feedback loop. Defaults to False (zero overhead).
    max_refinements (int): Max refinement rounds when validate=True. Defaults to 3.
    usage (TokenUsage, optional): Token usage accumulator. If provided,
        token counts from all LLM calls (including inference and
        validation retries) are added to it in-place.
    **kwargs: Additional args passed to LLM processor.

Returns:
    dict: Generated schema following the JsonElementExtractionStrategy format.

Raises:
    ValueError: If neither html nor url is provided.
r   )r  preprocess_html_for_schemaNr   z'Either 'html' or 'url' must be providedr:  r;  )AsyncWebCrawler)BrowserConfigCrawlerRunConfig	CacheModeT)headless	text_mode
light_mode)
cache_mode)config)rm   r  zFailed to fetch URL '': i  zHTTP z error for URL '')urlsr  r5  i  i  )r<   text_thresholdattr_value_thresholdmax_size)r   rV   r	  )rb  rc  r'  rm   rp  r   rO  user)roler   )r"  rz  rU  r#  r$  messagesr,  zLLM returned an empty responsezFailed to parse schema JSON: 	assistantz/Your response was not valid JSON. Parse error: z-
Please return ONLY valid JSON, nothing else.zFailed to generate schema: )r*  r/  r,  )	sort_keysz0Failed to generate schema: no attempts succeeded)9r  r  r  rF   rw  r   r  r  _GENERATE_SCHEMA_UNWANTED_PROPSr   localsrA  r*   async_webcrawlerr  async_configsr  r  r  BYPASSrH   r   r,  rY   error_messagestatus_coder.   	arun_manyr   rm   rL   r   formatrK   rj  ru  rm  JSONDecodeError	TypeErrorrh  rk  rv  r   r"  r#  r$  rZ  rp  r[  r\  rr  rs  r   r5   r:   rU  r`  )3r.   r)  rb  ro  r'  r"  r#  rm   rx  ry  rp  rj   r  r  rh   rs  original_htmlsr  r  r  r  browser_configcrawler_configr  crawlerrx   r  
html_partsr   cleanedheaderr*  
target_obj	first_urlinferredrf  r  prev_schema_jsonlast_schemarW  rX  r|  rg  r)  r  best_result	orig_htmlr]  current_jsonrY  feedbacks3                                                      r9   r}  .JsonElementExtractionStrategy.agenerate_schema  s    \ 	X <S[ZT-B-Bs3xST}FGG ;ZZ``bMDx~)$y6Gy%QRR c *,J  ?9QQ*N
 .9I9IJN 'sC00C5cD&n==t9>#*<<DGN<#SSF!>>'*?QyFL`L`Ka(bcc))S0'%0B0B/CCSTXYZT[S\\](^__!;;D&,kk]N$+$5$54$5$WWG!#J%.w%:	6%~~"+.CFJJ<sSYSgSgRh,i"jj!--4"+eF4F4F3GGWX^XbXbWccd,e"ff&--fkk:"<)/+/14%,	# "8!>!>Q!G"))VHBwi*@A &; ";;z2D5 >==8 #VN ;*S#..-!#%( 	D "!"5s;;%)ZZ0C%D
%8
&C&\&\]g&hO  	?'1#s';';QI!>!Q!Qzy`e "R "  &C&\&\]e&fO..2jj!.L+.CCDW\r#78xOQ?\*GH!A'00*0"&(22'00%%"  $++x~~/O/OO+''8>>+G+GG'&&(..*E*EE&&&q)1199#))++$%EFF$:3$?@$  K+	2CCI{$3 D  &"-?*@;OaCb*b"$Ki== , 9% ,**  ::f=L&*::K+ 5LLVWq[+H OO[SABOOVABI +N "JKKe >S X >===d ,,i8 $& '' 	7lQ.>#>#&CCF8$LMM EFEaS IC C!   H"=c!fX FGGHs   A-Z33A2Z3%W&Z3)&WWBW W!C1WZ3W9Z3AW2 AZ3XA9Z3-XXC-X3AZ38BZ3WWZ3W/W W/*Z32XZ3XZ3XZ0-AZ Z3Z0Z++Z00Z3)r)  rd   )r(  N)NN)Nr(  NNNNNNTr  N),rg   r   r   r   r   re   r   rH   r   rk   r   rr   r   r  r  r  r  r  r  r  r  r  r~   r  r  r  r  staticmethodrv  r   rU  r  r  r`  rh  rm  r  rv  r*   r   r  r}  r   r  r  s   @r9   r  r  Q  sT   > C	4tCH~ 	4++&)+	d38n	+Z       s  (<5Dn62	(:s :d3i :$tCQTH~BV :* C   C      s  $  !/3	AAA A "$s),	A
 
A AF C C C  C  	C 
 
C  C J / /3 /QT /dp /  }E  FJ  }K / /b (d (tCy ( (
 HI'#
 =;3 =;S =; =;be =;qt =; =;~  #'"3"5%) "E'E'E' E' !	E'
  E' E' E' 3S	>"E' E' E' E' 
E' E'N  #'"&%) "_L_L_L _L !	_L
  _L _L _L 3S	>"_L _L _L _L 
_L _Lr;   r  c                      ^  \ rS rSrSrS\\\4   4U 4S jjrS\4S jr	S\4S jr
S\4S	 jrS
\4S jrS
\4S jrS\4S jrS\4S jrSrU =r$ )rH  i  a  
Concrete implementation of `JsonElementExtractionStrategy` using CSS selectors.

How it works:
1. Parses HTML content with BeautifulSoup.
2. Selects elements using CSS selectors defined in the schema.
3. Extracts field data and applies transformations as defined.

Attributes:
    schema (Dict[str, Any]): The schema defining the extraction rules.
    verbose (bool): Enables verbose logging for debugging purposes.

Methods:
    _parse_html(html_content): Parses HTML content into a BeautifulSoup object.
    _get_base_elements(parsed_html, selector): Selects base elements using a CSS selector.
    _get_elements(element, selector): Selects child elements using a CSS selector.
    _get_element_text(element): Extracts text content from a BeautifulSoup element.
    _get_element_html(element): Extracts the raw HTML content of a BeautifulSoup element.
    _get_element_attribute(element, attribute): Retrieves an attribute value from a BeautifulSoup element.
r)  c                 2   > SUS'   [         TU ]  " U40 UD6  g Nr.   rb   r   rk   r  s      r9   rk   "JsonCssExtractionStrategy.__init__      !'~*6*r;   r<   c                     [        US5      $ )Nlxmlr,   r  s     r9   r  %JsonCssExtractionStrategy._parse_html  s    \622r;   r  c                 $    UR                  U5      $ r  selectr  s      r9   r  ,JsonCssExtractionStrategy._get_base_elements  s    !!(++r;   c                 $    UR                  U5      $ r  r  r  s      r9   r  'JsonCssExtractionStrategy._get_elements  s     ~~h''r;   r1   c                      UR                  SS9$ )NTr5   )get_textr  s     r9   r  +JsonCssExtractionStrategy._get_element_text#  s    d++r;   c                     [        U5      $ r  )rH   r  s     r9   r  +JsonCssExtractionStrategy._get_element_html&  s    7|r;   r  c                 $    UR                  U5      $ r  rI   r$  s      r9   r  0JsonCssExtractionStrategy._get_element_attribute)      {{9%%r;   r  c                    UR                  5       nUR                  S5      (       d  g USS  R                  5       nUR                  S5      nUS   R                  5       =(       d    S nUSS   Vs/ s H)  ofR                  5       (       d  M  UR                  5       PM+     nn0 nU(       a	  U4S jUS'   UR                  " U40 UD6$ s  snf )N+r   rA   r   c                 @   ^  T =(       a    [        U 4S jU 5       5      $ )Nc              3   ,   >#    U  H	  oT;   v   M     g 7fr  ro   )r8  clcs     r9   r:  NJsonCssExtractionStrategy._resolve_source.<locals>.<lambda>.<locals>.<genexpr>6  s      A"&Ba$rG  )r  )r  _clss   ` r9   r   ;JsonCssExtractionStrategy._resolve_source.<locals>.<lambda>6  s%    q 8S A"&A > 8r;   class_)r5   r  rJ   find_next_sibling)	ri   r  r  selr^  rG   prO   rj   s	            r9   r  )JsonCssExtractionStrategy._resolve_source,  s      %%QRj 		#Ahnn&$&+ABi=i77991779i=.5  F8 ((777 >s   /CCro   )rg   r   r   r   r   r   rH   r   rk   r  r  r  r  r  r  r  r   r  r  s   @r9   rH  rH    su    *+tCH~ +3 3, ,(s (
,C ,C & &8s 8 8r;   rH  c                      ^  \ rS rSrS\\\4   4U 4S jjrS\4S jrS r	S r
S rS	 rS
 rS rS\4S jrS\4S jrS\4S jrS\4S jrS\4S jrS\4S jrS rSrU =r$ )JsonLxmlExtractionStrategyi;  r)  c                    > SUS'   [         TU ]  " U40 UD6  0 U l        0 U l        0 U l        UR                  SS5      U l        UR                  SS5      U l        SSKJ	nJ
n  SSKJn  X0l	        X@l        XPl        g )	Nr.   rb   use_cachingToptimize_common_patternsr   )r/   r.   CSSSelector)r   rk   _selector_cache_xpath_cache_result_cacherI   r  r  r  r/   r.   lxml.cssselectr  html_parser)ri   r)  rj   r/   r.   r  rf   s         r9   rk   #JsonLxmlExtractionStrategy.__init__<  sy    !'~*6*! "::mT:(.

3Mt(T% 	%.
&r;   r<   c                     U R                   R                  SSS9nU R                   R                  X5      $ ! [         a  nU R                  (       a  [        SU 35         U R                  R                  U5      s SnA$ ! [         aI  nU R                  (       a  [        SU 35        U R                   R                  S5      s SnAs SnA$ SnAff = fSnAff = f)z&Parse HTML content with error recoveryT)recoverremove_blank_textz8Error parsing HTML, falling back to alternative method: NzCritical error parsing HTML: r.   )r/   
HTMLParserrX   rY   rd   r   r  Element)ri   r<   parserr  e2s        r9   r  &JsonLxmlExtractionStrategy._parse_htmlN  s    	2ZZ**44*PF::((>> 		2||PQRPSTU2''22<@@ 2<<9">?zz))&111	2		2sK   47 
CC!B;C
C9CCC	CCCCc                    U R                   (       d  U$ SSKnUR                  " SU5      (       a  U$ UR                  5       n[	        U5      S::  a  U$ [	        U5      S:  ab  [        S U 5       5      (       aK  U Vs/ s H2  oDR                  S5      (       d  UR                  S5      (       d  M0  UPM4     nnU(       a  US	   $ U$ s  snf )
z8Optimize common selector patterns for better performancer   Nztd:nth-child\(\d+\)r   r  c              3   r   #    U  H-  oR                  S 5      =(       d    UR                  S5      v   M/     g7f)rA   rC   N)r  )r8  r  s     r9   r:  @JsonLxmlExtractionStrategy._optimize_selector.<locals>.<genexpr>n  s*     !XRWQ,,s"3"Hq||C7H"HRWs   57rA   rC   )r  r6   r  rJ   r   r  r  )ri   selector_strr6   r^  r  specific_partss         r9   _optimize_selector-JsonLxmlExtractionStrategy._optimize_selector^  s    ,, 	99+\:: ""$u:? u:>c!XRW!XXX).YA,,s2C2Cq||TWGXaNY%b))	 Zs   =/C0Cc                 F  ^ ^^^^ TmT R                   (       a  T R                  T5      m T R                  T5      mTR                  mTT R                  T'   SUUUU U4S jjnU$ ! [
         a1  nT R                  (       a  [        ST SU 35        SS js SnA$ SnAff = f)z6Create a selector function that handles all edge casesc                 X  > S nTR                   (       aS  U R                  SS5      =(       d    [        [        U 5      5      nU ST 3nUTR                  ;   a  TR                  U   $ / n T" U 5      nU(       d  U(       a  TR                  TU 5      nU(       a  U R                  U5      nU(       d  ST;   a  TR                  U T5      nU(       d  TR                  U T5      nU(       d_  TR                  5       nU(       aH  US   n[        R                  " SU5      nU(       a%  UR                  S5      n	U R                  SU	 35      nTR                   (       a  U(       a  UTR                  U'   U$ ! [         a-  n
TR                  (       a  [        S	T S
U
 35         S n
A
U$ S n
A
ff = f)NrB   r3   z::	nth-childr  z^(\w+)r   .//Error applying selector 'r  )r  rI   rH   hashr  _make_context_sensitive_xpathrZ   _handle_nth_child_selector_fallback_class_id_searchrJ   r6   r	  r  rY   rd   r   )r  context_sensitive	cache_key
element_idr  context_xpathr^  	last_part	tag_matchtag_namer  compiledoriginal_selectorr  ri   rZ   s              r9   selector_funcKJsonLxmlExtractionStrategy._create_selector_function.<locals>.selector_func  s    	 ##!(T2!6!L#d7m:LJ#-,b ?I D$6$66#11)<<$P&w/G #'8(,(J(J5RY(Z(&-mmM&BG  ';:K+K&*&E&EgO`&aG  '&*&D&DWN_&`G  '$5$;$;$=E$,1"I	,.HHY	,J	#,/8q/AH.5mmc(<L.MG ''I8?**95 	 ! P|| 9,s1#NO	Ps   +DE2 2
F)<"F$$F)Error compiling selector 'r  c                     / $ r  ro   )r  r  s     r9   r   FJsonLxmlExtractionStrategy._create_selector_function.<locals>.<lambda>  s    2r;   N)T)r  r  r  pathr  rY   rd   r   )ri   r  r  r  r  r  rZ   s   ``  @@@r9   _create_selector_function4JsonLxmlExtractionStrategy._create_selector_functionu  s    ( ((22<@LE	>''5HMME /4Dl+3 3j !  	>||2<.A3GH >=	>s   9A% %
B /&BB B c                      UR                  S5      (       a  U$ UR                  S5      (       a  SU 3nOSU 3n UR                  U5        U$ !   SUR                  S5      S    3s $ = f!    g= f)z1Convert absolute XPath to context-sensitive XPathdescendant-or-self::r\  rA   r  r  N)r  rZ   rJ   )ri   rZ   r  r  s       r9   r  8JsonLxmlExtractionStrategy._make_context_sensitive_xpath  s    	 677 $$"#E7"%eW4m,$$4U[[-b1233	s'   A- !A- A A*(A- *A- -A1c                    SSK n/ n UR                  " SU5      nU(       a  UR                  S5      nUR                  SU S3S5      S   R	                  5       nU(       aF  UR                  " SU5      nU(       a  UR                  S5      OS	n	UR                  S
U SU	 35      nU$ UR                  S
U S35      nU$ ! [         a*  n
U R                  (       a  [        SU
 35         Sn
A
U$ Sn
A
ff = f)z2Special handling for nth-child selectors in tablesr   Ntd:nth-child\((\d+)\)r   ztd:nth-child(rc  r  z(\w+)r   .//td[]//]z#Error handling nth-child selector: )	r6   r  r  rJ   r5   rZ   rY   rd   r   )ri   r  r  r6   r  r	  col_numremaining_selectorr	  r
  r  s              r9   r  5JsonLxmlExtractionStrategy._handle_nth_child_selector  s    	AII6EE++a. &2%7%7-yPQ8RTU%VWY%Z%`%`%b"% !#		(4F GI5>yq1CH%mmfWIS
,KLG  &mmfWIQ,?@G
 	  	A||;A3?@		As   BB> 'B> >
C2C--C2c                    / n SSK nUR                  " SU5      nUR                  " SU5      nU H)  nUR                  SU S35      nUR                  U5        M+     U H)  n	UR                  SU	 S35      n
UR                  U
5        M+     U$ ! [         a*  nU R
                  (       a  [        S	U 35         SnAU$ SnAff = f)
z!Fallback to search by class or IDr   Nz\.([a-zA-Z0-9_-]+)z#([a-zA-Z0-9_-]+)z.//*[contains(@class, 'z')]z
.//*[@id='z']z#Error in fallback class/id search: )r6   findallrZ   rw   rY   rd   r   )ri   r  r  r  r6   class_matches
id_matches
class_nameclass_resultsid_name
id_resultsr  s               r9   r  4JsonLxmlExtractionStrategy._fallback_class_id_search  s    	AJJ'<lKM $8,GJ ,
 '0G
|SV.W X}- ,
 &$]]Zy+CD
z* & 	  	A||;A3?@		As   BB 
CB==Cc                 z    XR                   ;  a  U R                  U5      U R                   U'   U R                   U   $ )z.Get or create a selector function with caching)r  r  )ri   r  s     r9   _get_selector(JsonLxmlExtractionStrategy._get_selector	  s;    333151O1OP\1]D  .##L11r;   r  c                 2    U R                  U5      nU" USS9$ )r  Fr  r*  ri   r  r  r  s       r9   r  -JsonLxmlExtractionStrategy._get_base_elements	  s    **84[EBBr;   c                 2    U R                  U5      nU" USS9$ )z>Get child elements using the selector with context sensitivityTr-  r.  ri   r  r  r  s       r9   r  (JsonLxmlExtractionStrategy._get_elements	  s    **84W==r;   r1   c                     SR                  S UR                  S5       5       5      nU$ ! [         aS  nU R                  (       a  [	        SU 35         UR                  5       R                  5       s SnA$ !     SnAg= fSnAff = f)z$Extract normalized text from elementr   c              3   n   #    U  H+  oR                  5       (       d  M  UR                  5       v   M-     g 7fr  r  )r8  r  s     r9   r:  ?JsonLxmlExtractionStrategy._get_element_text.<locals>.<genexpr>(	  s"     W/I!WWYIAGGII/Is   55	.//text()zError extracting text: Nr3   )rK   rZ   rY   rd   r   text_contentr5   )ri   r  r0   r  s       r9   r  ,JsonLxmlExtractionStrategy._get_element_text$	  sv    	88Ww}}[/IWWDK 	||/s34++-3355	s2   (+ 
BBA82B8B :B BBc                      U R                   R                  USSS9$ ! [         a)  nU R                  (       a  [	        SU 35         SnAgSnAff = f)z)Get HTML string representation of elementunicoder.   )encodingr   zError serializing HTML: Nr3   )r/   tostringrY   rd   r   )ri   r  r  s      r9   r  ,JsonLxmlExtractionStrategy._get_element_html3	  sM    	::&&w6&RR 	||045	s    
AAAr  c                      UR                  U5      $ ! [         a,  nU R                  (       a  [        SU SU 35         SnAgSnAff = f)zGet attribute value safelyzError getting attribute 'r  N)rI   rY   rd   r   )ri   r  r  r  s       r9   r  1JsonLxmlExtractionStrategy._get_element_attribute<	  sF    	;;y)) 	||1)CsCD	s    
A	"AA	r  c                    UR                  5       nUR                  S5      (       d  g USS  R                  5       nUR                  S5      nUS   R                  5       =(       d    SnUSS   Vs/ s H)  ofR                  5       (       d  M  UR                  5       PM+     nnSU 3nU H  n	USU	 S3-  nM     US	-  nUR                  U5      n
U
(       a  U
S   $ S $ s  snf 
Nr  r   rA   r   r   z./following-sibling::z4[contains(concat(' ',normalize-space(@class),' '),' z ')]z[1]r5   r  rJ   rZ   ri   r  r  r  r^  rG   r  rO   rZ   clsr  s              r9   r  *JsonLxmlExtractionStrategy._resolve_sourceE	        %%QRj 		#Ahnn%#&+ABi=i77991779i='u-CKC5PTUUE --&$wqz.$. >   /CCc                 \    U R                   (       a  U R                  R                  5         gg)zClear caches to free memoryN)r  r  clear)ri   s    r9   _clear_caches(JsonLxmlExtractionStrategy._clear_cachesT	  s"    $$& r;   )r  r  r  r  r/   r  r  r  )rg   r   r   r   r   rH   r   rk   r  r  r  r  r  r  r*  r  r  r  r  r  r  rK  r   r  r  s   @r9   r  r  ;  s    'tCH~ '$2 2 .M>^.:62C C>s >
C C  /s /' 'r;   r  c                      ^  \ rS rSrS\\\4   4U 4S jjrS\4S jrS r	S\4S jr
S\4S	 jrS
\4S jrS
\4S jrS\4S jrS\4S jrSrU =r$ ) JsonLxmlExtractionStrategy_naiveiY	  r)  c                 @   > SUS'   [         TU ]  " U40 UD6  0 U l        g r  )r   rk   r  r  s      r9   rk   )JsonLxmlExtractionStrategy_naive.__init__Z	  s'    !'~*6*!r;   r<   c                 R    SSK Jn  UR                  " SS9nUR                  " X5      $ )Nr   r/   T)r  )r  r/   r  rX   )ri   r<   r/   r  s       r9   r  ,JsonLxmlExtractionStrategy_naive._parse_html_	  s&    !!$/55r;   c                 (  ^ ^^ TT R                   ;  a&  SSKJn   U" T5      mUUU 4S jnUT R                   T'   T R                   T   $ ! [         a>  nT R                  (       a  [        ST SU 35        S nUT R                   T'    SnANRSnAff = f)zCGet a selector function that works within the context of an elementr   r  c                   >  T
" U 5      nU(       a  U$ T
R                   nUR                  S5      (       a  UnOSUR                  S5       3nU R                  U5      nU(       a  U$ ST;   a  SS KnUR
                  " ST5      nU(       ag  UR                  S5      nTR                  SS5      S	   R                  5       nU(       a  U R                  S
U SU 35      $ U R                  S
U S35      $ TR                  5       n[        U5      S:  a!  US	   (       a  U R                  SUS	    35      $ / $ ! [         a.  n	TR                  (       a  [        ST SU	 35        / s S n	A	$ S n	A	ff = f)Nr  z./r\  r  r   r  r   rc  r  r  r  r  r  r  r  )r  r  lstriprZ   r6   r  r  rJ   r5   r   rY   rd   r   )r  r  rZ   r  r6   r	  r  sub_selectorr^  r  r  r  ri   s             r9   select_funcCJsonLxmlExtractionStrategy_naive._get_selector.<locals>.select_funcm	  sc   +""*7"3"#*N !) !++,BCC,1M /1c1B0C,DM")--">"#*N ',6%$&II.F$UE$*/++a./;/A/A#q/I"/M/S/S/U#/+2==6'#l^9\+]$]+2==6'!9L+M$M !- 2 2 4u:>eBi#*==3uRyk1B#CC!	$ "<<!$=l^3qc"RS!	"s<   D4 AD4 'A5D4 D4 2?D4 2D4 4
E,>#E'!E,'E,r  r  c                     / $ r  ro   )r  s    r9   fallback_funcEJsonLxmlExtractionStrategy_naive._get_selector.<locals>.fallback_func	  s    Ir;   N)r  r  r  rY   rd   r   )ri   r  r  rX  r  r[  r  s   ``    @r9   r*  .JsonLxmlExtractionStrategy_naive._get_selectord	  s    t3332<C&|4,"\ 6A$$\2 ##L11  C<<6|nCsKL 6C$$\2Cs   A	 	
B4BBr  c                 4    U R                  U5      nU" U5      $ r  r.  r/  s       r9   r  3JsonLxmlExtractionStrategy_naive._get_base_elements	  s    **84[))r;   c                 4    U R                  U5      nU" U5      $ r  r.  r2  s       r9   r  .JsonLxmlExtractionStrategy_naive._get_elements	  s    **84W%%r;   r1   c                 ^    SR                  UR                  S5      5      R                  5       $ Nr3   r7  rK   rZ   r5   r  s     r9   r  2JsonLxmlExtractionStrategy_naive._get_element_text	  #    www}}[1288::r;   c                 0    SSK Jn  UR                  " USS9$ )Nr   rR  r;  r<  )r  r/   r=  )ri   r  r/   s      r9   r  2JsonLxmlExtractionStrategy_naive._get_element_html	  s    ~~g	::r;   r  c                 $    UR                  U5      $ r  r  r$  s      r9   r  7JsonLxmlExtractionStrategy_naive._get_element_attribute	  r  r;   r  c                    UR                  5       nUR                  S5      (       d  g USS  R                  5       nUR                  S5      nUS   R                  5       =(       d    SnUSS   Vs/ s H)  ofR                  5       (       d  M  UR                  5       PM+     nnSU 3nU H  n	USU	 S3-  nM     US	-  nUR                  U5      n
U
(       a  U
S   $ S $ s  snf rB  rC  rD  s              r9   r  0JsonLxmlExtractionStrategy_naive._resolve_source	  rG  rH  )r  )rg   r   r   r   r   rH   r   rk   r  r*  r  r  r  r  r  r  r   r  r  s   @r9   rN  rN  Y	  sw    "tCH~ "
6 6
B2H* *&s &;C ;;C ;& &/s / /r;   rN  c                      ^  \ rS rSrSrS\\\4   4U 4S jjrS\4S jr	S\4S jr
S	\S
\4S jrS	\S
\4S jrS\4S jrS
\4S jrS
\4S jrS\4S jrS\4S jrSrU =r$ )rI  i	  a  
Concrete implementation of `JsonElementExtractionStrategy` using XPath selectors.

How it works:
1. Parses HTML content into an lxml tree.
2. Selects elements using XPath expressions.
3. Converts CSS selectors to XPath when needed.

Attributes:
    schema (Dict[str, Any]): The schema defining the extraction rules.
    verbose (bool): Enables verbose logging for debugging purposes.

Methods:
    _parse_html(html_content): Parses HTML content into an lxml tree.
    _get_base_elements(parsed_html, selector): Selects base elements using an XPath selector.
    _css_to_xpath(css_selector): Converts a CSS selector to an XPath expression.
    _get_elements(element, selector): Selects child elements using an XPath selector.
    _get_element_text(element): Extracts text content from an lxml element.
    _get_element_html(element): Extracts the raw HTML content of an lxml element.
    _get_element_attribute(element, attribute): Retrieves an attribute value from an lxml element.
r)  c                 2   > SUS'   [         TU ]  " U40 UD6  g r  r  r  s      r9   rk   $JsonXPathExtractionStrategy.__init__	  r  r;   r<   c                 .    [         R                  " U5      $ r  )r.   rX   r  s     r9   r  'JsonXPathExtractionStrategy._parse_html	  s    |,,r;   r  c                 $    UR                  U5      $ r  )rZ   r  s      r9   r  .JsonXPathExtractionStrategy._get_base_elements	  s      **r;   css_selectorr1   c                 4    SU;   a  U$ U R                  U5      $ )z'Convert CSS selector to XPath if neededr\  )_basic_css_to_xpath)ri   ru  s     r9   _css_to_xpath)JsonXPathExtractionStrategy._css_to_xpath	  s!    ,''55r;   c                     SU;   a%  UR                  S5      nSSR                  U5      -   $ SU;   a%  UR                  S5      nSSR                  U5      -   $ SU-   $ )z.Basic CSS to XPath conversion for common casesz > z//r\  r   )rJ   rK   )ri   ru  r^  s      r9   rw  /JsonXPathExtractionStrategy._basic_css_to_xpath	  sd    L  &&u-E#((5/)), &&s+E$))E***l""r;   c                 |    U R                  U5      nUR                  S5      (       d  SU-   nUR                  U5      $ )NrA   )rx  r  rZ   )ri   r  r  rZ   s       r9   r  )JsonXPathExtractionStrategy._get_elements	  s;    ""8,$$%KE}}U##r;   c                 ^    SR                  UR                  S5      5      R                  5       $ rc  rd  r  s     r9   r  -JsonXPathExtractionStrategy._get_element_text 
  rf  r;   c                 ,    [         R                  " USS9$ )Nr;  rh  )r/   r=  r  s     r9   r  -JsonXPathExtractionStrategy._get_element_html
  s    ~~g	::r;   r  c                 $    UR                  U5      $ r  r  r$  s      r9   r  2JsonXPathExtractionStrategy._get_element_attribute
  r  r;   r  c                    UR                  5       nUR                  S5      (       d  g USS  R                  5       nUR                  S5      nUS   R                  5       =(       d    SnUSS   Vs/ s H)  ofR                  5       (       d  M  UR                  5       PM+     nnSU 3nU H  n	USU	 S3-  nM     US	-  nUR                  U5      n
U
(       a  U
S   $ S $ s  snf rB  rC  rD  s              r9   r  +JsonXPathExtractionStrategy._resolve_source	
  rG  rH  ro   )rg   r   r   r   r   r   rH   r   rk   r  r  rx  rw  r  r  r  r  r  r   r  r  s   @r9   rI  rI  	  s    ,+tCH~ +- -+ +6# 6# 6# # #$s $;C ;;C ;& &/s / /r;   rI      z	
z\x02xz\x08z(?<!\\)\\(?![\\u])r)  c                 L   0 nU R                  5        H_  u  p#[        R                  SU5      R                  [        5      n[
        R                  SU5      n [        R                  " U5        X1U'   Ma     U$ ! [        R                   a  n[        SU SU 35      SeSnAff = f)z>Fix common JSON-escape goofs coming from LLMs or manual edits.z\\b\\\\zRegex for 'u   ' won’t compile after fix: N)
r   _WB_FIXr7   	translate_CTRL_NEEDS_ESCAPEr6   r  ra  r  )r)  safer  patr  s        r9   _sanitize_schemar  "
  s    Dlln
kk&#&007 -	^JJsO U % K	 xx 	^{5'1NqcRSY]]	^s   A88B#BB#c                      ^  \ rS rSr% Sr " S S\5      r\R                  r\R                  r
\R                  r\R                  r\R                  r\R"                  r\R&                  r\R*                  r\R.                  r\R2                  r\R6                  r\R:                  r\R>                  r \RB                  r"\RF                  r$\RJ                  r&\RN                  r(\RR                  r*\RV                  r,\RZ                  r.\R^                  r0\Rb                  r2\" S5      r30 SS_SS	_S
S_SS_SS_SS_SS_SS_SS_SS_SS_SS_SS_S S!_S"S#_S$S%_S&S'_S(S)S*S+S,.Er4\5\6\64   \7S-'   \8Rr                  \8Rt                  -  r;S.S.S/.r<\Rz                  4S0S1S2.S3SS4\>\?\5\6\64   \@\A\6\64      4      S5\6S6S04U 4S7 jjjjrBS\6S8\6S6\@\5\6\C4      4S9 jrDS8\6S6\64S: jrE\FS0S0S0S;.S<\6S=\6S>\>\6   S?\>\@\6      S@\>\G   S6\5\6\64   4SA jj5       rHSBrIU =rJ$ )CRegexExtractionStrategyi6
  ap  
A lean strategy that finds e-mails, phones, URLs, dates, money, etc.,
using nothing but pre-compiled regular expressions.

Extraction returns::

    {
        "url":   "<page-url>",
        "label": "<pattern-label>",
        "value": "<matched-string>",
        "span":  [start, end]
    }

Only `generate_schema()` touches an LLM, extraction itself is pure Python.
c                      \ rS rSr\" 5       r\" 5       r\" 5       r\" 5       r\" 5       r	\" 5       r
\" 5       r\" 5       r\" 5       r\" 5       r\" 5       r\" 5       r\" 5       r\" 5       r\" 5       r\" 5       r\" 5       r\" 5       r\" 5       r\" 5       r\" 5       r\" 5       r\\-  \-  \-  \	-  \
-  \-  \-  \-  \-  \-  \-  \-  \-  \-  \-  \-  \-  \-  \-  \-  rSrg)RegexExtractionStrategy._BiJ
  ro   N)rg   r   r   r   r   EMAIL
PHONE_INTLPHONE_USrL  IPV4IPV6UUIDCURRENCY
PERCENTAGENUMBERDATE_ISODATE_USTIME_24H	POSTAL_US	POSTAL_UKHTML_COLOR_HEXTWITTER_HANDLEHASHTAGMAC_ADDRIBANCREDIT_CARDNOTHINGALLr   ro   r;   r9   _Br  J
  s=   &&
&&&&&&&
&&&&&	&	&&&&&&&J)C/$6=D#$&,-/78:ABDLM#$&457EF  !! $(( +66 	r;   r  r   emailz[\w.+-]+@[\w-]+\.[\w.-]+
phone_intlz\+?\d[\d .()-]{7,}\dphone_usz#\(?\d{3}\)?[ -. ]?\d{3}[ -. ]?\d{4}rm   zhttps?://[^\s\"'<>]+ipv4z(?:\d{1,3}\.){3}\d{1,3}ipv6z"[A-F0-9]{1,4}(?::[A-F0-9]{1,4}){7}uuidzG[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}currencyu,   (?:USD|EUR|RM|\$|€|£)\s?\d+(?:[.,]\d{2})?
percentagez\d+(?:\.\d+)?%numberz%\b\d{1,3}(?:[,.\s]\d{3})*(?:\.\d+)?\bdate_isoz\d{4}-\d{2}-\d{2}date_usz\d{1,2}/\d{1,2}/\d{2,4}time_24hz.\b(?:[01]?\d|2[0-3]):[0-5]\d(?:[:.][0-5]\d)?\b	postal_usz\b\d{5}(?:-\d{4})?\b	postal_ukz$\b[A-Z]{1,2}\d[A-Z\d]? ?\d[A-Z]{2}\bhtml_color_hexz#[0-9A-Fa-f]{6}\btwitter_handlez@[\w]{1,15}z#[\w-]+z$(?:[0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}z[A-Z]{2}\d{2}[A-Z0-9]{11,30}zH\b(?:4\d{12}(?:\d{3})?|5[1-5]\d{14}|3[47]\d{13}|6(?:011|5\d{2})\d{12})\b)hashtagmac_addribancredit_cardDEFAULT_PATTERNSzUse llm_config insteadr+  Nfit_html)customrb   r  r  rb   r1   c          	      T  > [         T	U ]  " SSU0UD6  U R                  R                  5        VVs0 s H?  u  pV[	        U R
                  UR                  5       5      R                  U-  (       d  M=  XV_MA     nnnU(       aM  [        U[        5      (       a  UR                  U5        O&UR                  U VVs0 s H  u  pX_M	     snn5        UR                  5        VVs0 s H&  u  pU[        R                  " X`R                  5      _M(     snnU l        gs  snnf s  snnf s  snnf )z
Args:
    patterns: Custom patterns overriding or extending defaults.
              Dict[label, regex] or list[tuple(label, regex)].
    input_format: "html", "markdown" or "text".
    **kwargs: Forwarded to ExtractionStrategy.
rb   Nro   )r   rk   r  r   rt  r  r  rC  rF   rv  r  r6   r  _FLAGS	_compiled)
ri   r  r  rb   rj   r   rxmergedlblrf   s
            r9   rk    RegexExtractionStrategy.__init__
  s     	=l=f=
  00668"
8tww		,22W< CG8 	 "
 &$''f%f=f73swf=> =CLLN.
<JCB,,N.
"
 >.
s   <D1D8D
!-D$r   c           
         / nU R                   R                  5        H`  u  pgUR                  U5       HF  nUR                  UUUR	                  S5      UR                  5       UR                  5       /S.5        MH     Mb     U$ )Nr   )rm   r  rC  span)r  r   finditerrL   r  startend)	ri   rm   r   rq   kwoutr  crems	            r9   rr   RegexExtractionStrategy.extract
  sp    $&....0JE\\'*

"!&!"!"AEEG 4	 + 1 
r;   c                 Z    U R                   S:X  a  U$ [        US5      R                  SSS9$ )Nr0   r  r   Tr  )rb   r-   r  )ri   r   s     r9   _plain_text#RegexExtractionStrategy._plain_text
  s2    &NWf-66s$6GGr;   )rb  examplesr'  r  r.   rb  r  r'  c          	         [         R                   H)  nXe;   d  M
  [        U S[         R                  U    35      e   Uc
  [        5       nSU  SU  S3nSUSS S/nU(       a"  UR	                  S	UR                  5        35        U(       a&  UR	                  S
SR                  USS 5      -   5        SR                  U5      n	[        UR                  SR                  Xy/5      SUR                  UR                  US9n
U
R                  S   R                  R                  nUR                  SS5      n[        R                   " SSU5      n ["        R$                  " U5      nUR+                  5        H  u  p [        R,                  " U5        M     U$ ! [&         a  n[)        SU 35      UeSnAff = f! [        R.                   a  n[)        SU SU 35      SeSnAff = f)u   
Ask an LLM for a single page-specific regex and return
    {label: pattern}   ── ready for RegexExtractionStrategy(custom=…)
z is deprecated, NzaYou are an expert Python-regex engineer.
Return **one** JSON object whose single key is exactly "u  ", and whose value is a raw-string regex pattern that works with the standard `re` module in Python.

Strict rules (obey every bullet):
• If a *user query* is supplied, treat it as the precise semantic target and optimise the   pattern to capture ONLY text that answers that query. If the query conflicts with the   sample HTML, the HTML wins.
• Tailor the pattern to the *sample HTML* – reproduce its exact punctuation, spacing,   symbols, capitalisation, etc. Do **NOT** invent a generic form.
• Keep it minimal and fast: avoid unnecessary capturing, prefer non-capturing `(?: … )`,   and guard against catastrophic backtracking.
• Anchor with `^`, `$`, or `\b` only when it genuinely improves precision.
• Use inline flags like `(?i)` when needed; no verbose flag comments.
• Output must be valid JSON – no markdown, code fences, comments, or extra keys.
• The regex value must be a Python string literal: **double every backslash** (e.g. `\\b`, `\\d`, `\\\\`).

Example valid output:
{"z5": "(?:RM|rm)\\s?\\d{1,3}(?:,\\d{3})*(?:\\.\\d{2})?"}z```htmli  z```z

## Query
z## Examples
rV   r  r	  T)r"  rz  rU  r#  r$  r,  r   z\bz(?<!\\)\\(?![\\u"])r  zLLM did not return valid JSON: zInvalid regex for 'r  )r  r?  rA  r*   rL   r5   rK   r   r"  r#  r$  rr  rs  r   rl  r6   r7   rj  ru  rY   r  r   r  ra  )r  r.   rb  r  r'  rj   k
system_msg
user_partsuser_msgresprg  pattern_dictexcr  r  r  s                    r9   generate_pattern(RegexExtractionStrategy.generate_pattern
  s     )88A{$c)*A*Q*QRS*T)UV  9 *,JHHMw O" 'b%d 	.  ete4
u{{}o>?o		(3B-0HHI;;z* /(("(++z.D"E **((
 ll1o%%--kk&%(ff+Wc:	O::c?L
 %**,HCN

3 -   	O>seDE3N	O 88 N #6se3qc!BCMNs0   F 9F6
F3F..F36G!
GG!)r  )Krg   r   r   r   r   r   r  r  Emailr  	PhoneIntlr  PhoneUSrL  Urlr  IPv4r  IPv6r  Uuidr  Currencyr  
Percentager  Numberr  DateIsor  DateUSr  Time24hr  PostalUSr  PostalUKr  HexColorr  TwitterHandler  Hashtagr  MacAddrr  Ibanr  
CreditCardr  AllNothingr  r   rH   __annotations__r6   
IGNORECASEr8   r  r?  r  r   r   r   r	   rk   r   rr   r  r  r)   r  r   r  r  s   @r9   r  r  6
  s1   &
W 
> XXE]]I[[GVVCWWDWWDWWD[[H]]JYYF[[GZZF[[G\\H\\H&&H&&MZZG[[GWWD^^JVVCUG
(6( 	2( 	A	( 	2( 	5( 	@( 	e( 	J( 	,( 	C(  	/!(" 	5#($ 	L%(( 	2)(* 	B+(, 	/-(. 	>/(0 &B:f7(d38n < ]]R\\)F,-O 

!
 JN&!
!
 tCH~tE#s(O/DDEF	!

 !
 
!
 !
L3  4S#X;O &H3 H3 H 
  $(,*.SSS }	S
 49%S Y'S 
c3hS Sr;   r  )r  )labcr   r   r  r<  typingr   r   r   r   r	   r
   r   r~  r   r   rj  r   enumr   r   rr  r   r   r   r   r   r  r   r   r   r   r   r   r  r   r   r   r    r!   r"   r#   modelsr$   model_loaderr%   r&   r'   r(   typesr)   r*   	functoolsr+   r   r   r6   bs4r-   r  r.   r/   rH   r:   r  r^   r`   r   r   r  r  r  r   r  r  r  r   r  r  r  rw  rv  r  r  r   r   r  r  r  r  r   rF   r   r  r  r  rH  r  rN  rI  chrordr  r  r  r  r  r  )r  s   0r9   <module>r     s   # 
  C C C ?    v  v         0   	    !3 !3 !s !8<N <N~
- 
.cD' cDRv. v|		3	s	#U	,2D		3		 %s	,13	8=s	 
3	 &	 #-h	 D		 $		 !(		 16s		
 	
 $'sfsU5$d	 .Qc .Qt .Q .QbjL$6 jLX<8 = <8|\'!> \'|n/'D n/`M/"? M/^
 ),Cr(;Q(;1?P	r#a&	(;Q
**W


01T#s(^ S#X (M0 M3 	Rs   		G-G-