
    Mi:                   l   % S r SSKJr  SSKrSSKrSSKrSSKJr  SSKJ	r	J
r
JrJrJrJr  SSKrSSKJrJr  SSKJr  SSKJrJr  SS	KJrJrJr  SS
KJrJrJrJ r J!r!J"r"J#r#J$r$  SSK%J&r&  Sr'S\(S'    Sr)S\(S'    \
\ /\*4   r+S\(S'    \,\-\-4   r.S\(S'    " S S5      r/ " S S5      r0S4S jr1S5S jr2      S6S jr3 " S S5      r4 " S S5      r5 " S  S!5      r6 " S" S#5      r7 " S$ S%5      r8 " S& S'5      r9 " S( S)5      r: " S* S+5      r; " S, S-5      r< " S. S/5      r= " S0 S15      r>S7S2 jr?S4S3 jr@g)8z@Chunking objects not specific to a particular chunking strategy.    )annotationsN)cached_property)AnyCallableDefaultDictIterableIteratorcast)ParserErrortostring)fragment_fromstring)Self	TypeAlias)HtmlCellHtmlRow	HtmlTable)CodeSnippetCompositeElementConsolidationStrategyElementElementMetadataTable
TableChunkTitle)loggeri  intCHUNK_MAX_CHARS_DEFAULTTboolCHUNK_MULTI_PAGE_DEFAULTr   BoundaryPredicateTextAndHtmlc                  <    \ rS rSrSrSS jr\S 5       rS	S jrSr	g)
TokenCounter6   zyToken counting using tiktoken for token-based chunking.

Lazily imports tiktoken only when token counting is first used.
c                    Xl         g N_tokenizer_nameself	tokenizers     t/var/www/eduai.edurigo.com/storigo/production/storigo_env/lib/python3.13/site-packages/unstructured/chunking/base.py__init__TokenCounter.__init__<   s    (    c                    SSK n UR                  U R                  5      $ ! [         a    UR	                  U R                  5      s $ f = f)z'Lazily initialize the tiktoken encoder.r   N)tiktokenencoding_for_modelr(   KeyErrorget_encoding)r*   r1   s     r,   _encoderTokenCounter._encoder?   sI     		?..t/C/CDD 	?(()=)=>>	?s   ! %A	A	c                J    [        U R                  R                  U5      5      $ )z&Return the number of tokens in `text`.)lenr5   encoder*   texts     r,   countTokenCounter.countK   s    4==''-..r/   r'   N)r+   strr;   r>   returnr   )
__name__
__module____qualname____firstlineno____doc__r-   r   r5   r<   __static_attributes__ r/   r,   r#   r#   6   s%    
) 	? 	?/r/   r#   c                  b   \ rS rSrSrSS jr\SS j5       r\SS j5       r	\SS j5       r
\SS j5       r\SS j5       r\SS	 j5       r\SS
 j5       r\SS j5       r\SS j5       r\SS j5       r\SS j5       r\SS j5       r\SS j5       r\SS j5       r\SS j5       rS S jrS!S jrSrg)"ChunkingOptionsU   a  Specifies parameters of optional chunking behaviors.

    Parameters
    ----------
    max_characters
        Hard-maximum text-length of chunk. A chunk longer than this will be split mid-text and be
        emitted as two or more chunks. Mutually exclusive with `max_tokens`.
    max_tokens
        Hard-maximum token count of chunk. A chunk with more tokens than this will be split mid-text
        and be emitted as two or more chunks. Requires `tokenizer` to be specified. Mutually
        exclusive with `max_characters`.
    new_after_n_chars
        Preferred approximate chunk size. A chunk composed of elements totalling this size or
        greater is considered "full" and will not be enlarged by adding another element, even if it
        will fit within the remaining `max_characters` for that chunk. Defaults to `max_characters`
        when not specified, which effectively disables this behavior. Specifying 0 for this
        argument causes each element to appear in a chunk by itself (although an element with text
        longer than `max_characters` will be still be split into two or more chunks).
    new_after_n_tokens
        Token-based equivalent of `new_after_n_chars`. Preferred approximate chunk size in tokens.
        Requires `tokenizer` and `max_tokens` to be specified.
    combine_text_under_n_chars
        Provides a way to "recombine" small chunks formed by breaking on a semantic boundary. Only
        relevant for a chunking strategy that specifies higher-level semantic boundaries to be
        respected, like "section" or "page". Recursively combines two adjacent pre-chunks when the
        first pre-chunk is smaller than this threshold. "Recursively" here means the resulting
        pre-chunk can be combined with the next pre-chunk if it is still under the length threshold.
        Defaults to `max_characters` which combines chunks whenever space allows. Specifying 0 for
        this argument suppresses combining of small chunks. Note this value is "capped" at the
        `new_after_n_chars` value since a value higher than that would not change this parameter's
        effect.
    overlap
        Specifies the length of a string ("tail") to be drawn from each chunk and prefixed to the
        next chunk as a context-preserving mechanism. By default, this only applies to split-chunks
        where an oversized element is divided into multiple chunks by text-splitting.
    overlap_all
        Default: `False`. When `True`, apply overlap between "normal" chunks formed from whole
        elements and not subject to text-splitting. Use this with caution as it entails a certain
        level of "pollution" of otherwise clean semantic chunk boundaries.
    repeat_table_headers
        Default: `True`. When `True`, repeated table-header behavior is enabled for chunked table
        continuations. Specify `False` to opt out and preserve legacy table-chunk behavior.
    text_splitting_separators
        A sequence of strings like `("
", " ")` to be used as target separators during
        text-splitting. Text-splitting only applies to splitting an oversized element into two or
        more chunks. These separators are tried in the specified order until one is found in the
        string to be split. The default separator is `""` which matches between any two characters.
        This separator should not be specified in this sequence because it is always the separator
        of last-resort. Note that because the separator is removed during text-splitting, only
        whitespace character sequences are suitable.
    tokenizer
        The tokenizer to use for token-based chunking. Can be either an encoding name (e.g.,
        "cl100k_base") or a model name (e.g., "gpt-4"). Required when using `max_tokens`.
    c                    Xl         g r&   _kwargs)r*   kwargss     r,   r-   ChunkingOptions.__init__   s    r/   c                6    U " S0 UD6nUR                  5         U$ )zUReturn instance or raises `ValueError` on invalid arguments like overlap > max_chars.rG   )	_validate)clsrN   r*   s      r,   newChunkingOptions.new   s     }V}r/   c                    g)zThe semantic-boundary detectors to be applied to break pre-chunks.

Overridden by sub-typs to provide semantic-boundary isolation behaviors.
rG   rG   r*   s    r,   boundary_predicates#ChunkingOptions.boundary_predicates   s     r/   c                F    U R                   R                  S5      nUb  U$ S$ )zCombine two consecutive text pre-chunks if first is smaller than this and both will fit.

Default applied here is `0` which essentially disables chunk combining. Must be overridden
by subclass where combining behavior is supported.
combine_text_under_n_charsr   rM   getr*   	arg_values     r,   rZ   *ChunkingOptions.combine_text_under_n_chars   s)     LL$$%AB	%1y8q8r/   c                    U R                   (       a  U R                  S   $ U R                  R                  S5      nUb  U$ [        $ )a  The maximum size for a chunk (in characters or tokens depending on mode).

A pre-chunk will only exceed this size when it contains exactly one element which by itself
exceeds this size. Such a pre-chunk is subject to mid-text splitting later in the chunking
process.

max_tokensmax_characters)use_token_countingrM   r\   r   r]   s     r,   hard_maxChunkingOptions.hard_max   sB     ""<<--LL$$%56	%1yN7NNr/   c                X    U R                   R                  S5      nUc  S$ [        U5      $ )zqWhen True, add original elements from pre-chunk to `.metadata.orig_elements` of chunk.

Default value is `True`.
include_orig_elementsTrM   r\   r   r]   s     r,   rg   %ChunkingOptions.include_orig_elements   s-     LL$$%<=	 (t=d9o=r/   c                X    U R                   R                  S5      nUc  S$ [        U5      $ )zaWhen True, repeat detected table headers in continuation table chunks.

Default value is `True`.
repeat_table_headersTrh   r]   s     r,   rk   $ChunkingOptions.repeat_table_headers   s-     LL$$%;<	 (t=d9o=r/   c                X    U R                   R                  S5      nUc  S$ [        U5      $ )zZWhen True, Table elements are passed through without chunking.

Default value is `False`.
skip_table_chunkingFrh   r]   s     r,   rn   #ChunkingOptions.skip_table_chunking   s-     LL$$%:;	!)u>tI>r/   c                b    U R                   R                  S5      nU(       a  U R                  $ S$ )zCharacters of overlap to add between chunks.

This applies only to boundaries between chunks formed from whole elements and not to
text-splitting boundaries that arise from splitting an oversized element.
overlap_allr   )rM   r\   overlap)r*   overlap_all_args     r,   inter_chunk_overlap#ChunkingOptions.inter_chunk_overlap   s)     ,,**=9.t||5A5r/   c                N    U R                   R                  S5      nU=(       d    S$ )zThe number of characters to overlap text when splitting chunks mid-text.

The actual overlap will not exceed this number of characters but may be less as required to
respect splitting-character boundaries.
rr   r   r[   )r*   overlap_args     r,   rr   ChunkingOptions.overlap   s#     ll&&y1ar/   c                    U R                   nU R                  (       a)  U R                  R                  S5      nUc  U$ X!:  a  U$ U$ U R                  R                  S5      nUc  U$ X1:  a  U$ U$ )zA pre-chunk of this size or greater is considered full.

Note that while a value of `0` is valid, it essentially disables chunking by putting
each element into its own chunk.
new_after_n_tokensnew_after_n_chars)rd   rc   rM   r\   )r*   rd   new_after_n_tokens_argnew_after_n_chars_args       r,   soft_maxChunkingOptions.soft_max   s     ==""%)\\%5%56J%K"%-%0)) $ 0 01D E !(O !+O %$r/   c                    [        U 5      $ )zA text-splitting function suitable for splitting the text of an oversized pre-chunk.

The function is pre-configured with the chosen chunking window size and any other applicable
options specified by the caller as part of this chunking-options instance.
)_TextSplitterrV   s    r,   splitChunkingOptions.split  s     T""r/   c                    g)a;  The string to insert between elements when concatenating their text for a chunk.

        Right now this is just "

" (a blank line in plain text), but having this here rather
        than as a module-level constant provides a way for us to easily make it user-configurable
        in future if we want to.
        z

rG   rV   s    r,   text_separatorChunkingOptions.text_separator  s     r/   c                X    U R                   R                  S5      nUc  S$ [        U5      $ )zLSequence of text-splitting target strings to be used in order of preference.text_splitting_separators)
 )rM   r\   tuple)r*   text_splitting_separators_args     r,   r   )ChunkingOptions.text_splitting_separators  s<     )-(8(89T(U% -4 	
 45	
r/   c                `    U R                   R                  S5      nU(       a  [        U5      $ S$ )zQThe token counter for token-based chunking, or None for character-based chunking.r+   N)rM   r\   r#   r)   s     r,   token_counterChunkingOptions.token_counter$  s*     LL$$[1	*3|I&==r/   c                <    U R                   R                  S5      SL$ )zHTrue when token-based chunking is configured, False for character-based.ra   Nr[   rV   s    r,   rc   "ChunkingOptions.use_token_counting*  s     ||-T99r/   c                    U R                   (       a,  U R                  (       a  U R                  R                  U5      $ [        U5      $ )zIReturn the size of `text` in the configured units (characters or tokens).)rc   r   r<   r8   r:   s     r,   measureChunkingOptions.measure/  s4    ""t'9'9%%++D114yr/   c                   U R                   R                  S5      nU R                   R                  S5      nU R                   R                  S5      nUb  Ub  [        S5      eUb  Uc  [        S5      eUb  US::  a  [        SU 35      eU R                   R                  S	5      nUb  Uc  [        S
5      eUb  US:  a  [        SU 35      eU R                  nUS::  a  [        SU 35      eU R                   R                  S5      nUb  US:  a  [        SU 35      eU R                  U:  a  [        SU R                   SU 35      eg)z5Raise ValueError if requestion option-set is invalid.ra   rb   r+   Nz\'max_tokens' and 'max_characters' are mutually exclusive; specify one or the other, not bothz/'tokenizer' is required when using 'max_tokens'r   z''max_tokens' argument must be > 0, got rz   z:'new_after_n_tokens' requires 'max_tokens' to be specifiedz0'new_after_n_tokens' argument must be >= 0, got z+'max_characters' argument must be > 0, got r{   z/'new_after_n_chars' argument must be >= 0, got z;'overlap' argument must be less than `max_characters`, got z >= )rM   r\   
ValueErrorrd   rr   )r*   ra   rb   r+   rz   rd   r{   s          r,   rQ   ChunkingOptions._validate5  s   \\%%l3
))*:;LL$$[1	 !n&@6  !i&7NOO !jAoFzlSTT "\\--.BC)j.@YZZ ).@1.DBCUBVW 
 ==q=J8*UVV !LL,,-@A(->-BNO`Nabcc <<8#~T(5  $r/   rL   N)rN   r   )rN   r   r@   r   r@   ztuple[BoundaryPredicate, ...]r@   r   r@   r   )r@   z Callable[[str], tuple[str, str]]r@   r>   )r@   ztuple[str, ...])r@   zTokenCounter | Noner?   )r@   None)rA   rB   rC   rD   rE   r-   classmethodrS   r   rW   rZ   rd   rg   rk   rn   rt   rr   r~   r   r   r   r   rc   r   rQ   rF   rG   r/   r,   rI   rI   U   sE   5n     9 9 O O > > > > ? ? 6 6     % %> # #   
 
 > >
 : :0r/   rI   c                "    [        U [        5      $ )a  True when ``element`` is a `Table` or a concrete subtype such as `TableChunk`.

Subclasses share the same isolation contract: they must not share a pre-chunk with arbitrary
text elements, and two table-bearing sequences must not be merged by `PreChunkCombiner`.
)
isinstancer   elements    r,   _element_is_table_familyr   p  s     gu%%r/   c                &    [        S U  5       5      $ )zJTrue when ``elements`` already includes at least one table-family element.c              3  8   #    U  H  n[        U5      v   M     g 7fr&   )r   ).0es     r,   	<genexpr>1_elements_contain_table_family.<locals>.<genexpr>{  s     =Hq'**H   )any)elementss    r,   _elements_contain_table_familyr   y  s    =H===r/   c                <    [        U 5      =(       d    [        U5      $ )a/  True when a proposed merge of two element streams must be rejected for table isolation.

If either side already contains a table, the combiner must flush before accepting the other
side. This keeps `combine_text_under_n_chars` from concatenating a table pre-chunk with
neighboring narrative pre-chunks.
)r   )leftrights     r,   +_table_isolation_forbids_side_by_side_merger   ~  s     *$/X3QRW3XXr/   c                  j    \ rS rSrSrS
S jr\      SS j5       rSS jr\	SS j5       r
SS jrSrg	)
PreChunkeri  a9  Gathers sequential elements into pre-chunks as length constraints allow.

The pre-chunker's responsibilities are:

- **Segregate semantic units.** Identify semantic unit boundaries and segregate elements on
  either side of those boundaries into different sections. In this case, the primary indicator
  of a semantic boundary is a `Title` element. A page-break (change in page-number) is also a
  semantic boundary when `multipage_sections` is `False`.

- **Minimize chunk count for each semantic unit.** Group the elements within a semantic unit
  into sections as big as possible without exceeding the chunk window size.

- **Minimize chunks that must be split mid-text.** Precompute the text length of each section
  and only produce a section that exceeds the chunk window size when there is a single element
  with text longer than that window.

A Table element is placed into a section by itself. CheckBox elements are dropped.

The "by-title" strategy specifies breaking on section boundaries; a `Title` element indicates
a new "section", hence the "by-title" designation.
c                    Xl         X l        g r&   	_elements_opts)r*   r   optss      r,   r-   PreChunker.__init__  s    !
r/   c                .    U " X5      R                  5       $ )zEGenerate pre-chunks from the element-stream provided on construction.)_iter_pre_chunks)rR   r   r   s      r,   iter_pre_chunksPreChunker.iter_pre_chunks  s    
 8"3355r/   c              #  >  #    [        U R                  5      nU R                   HX  nU R                  U5      (       d  UR	                  U5      (       d  UR                  5        Sh  vN   UR                  U5        MZ     UR                  5        Sh  vN   g N2 N7f)a  Generate pre-chunks from the element-stream provided on construction.

A *pre-chunk* is the largest sub-sequence of elements that will both fit within the
chunking window and respects the semantic boundary rules of the chunking strategy. When a
single element exceeds the chunking window size it is placed in a pre-chunk by itself and
is subject to mid-text splitting in the second phase of the chunking process.
N)PreChunkBuilderr   r   _is_in_new_semantic_unitwill_fitflushadd_element)r*   pre_chunk_builderr   s      r,   r   PreChunker._iter_pre_chunks  s      ,DJJ7~~G --g66(11'::,22444 ))'2 & %**,,, 5 	-s$   A$B&B',BBBBc                .    U R                   R                  $ )zBThe semantic-boundary detectors to be applied to break pre-chunks.)r   rW   rV   s    r,   _boundary_predicatesPreChunker._boundary_predicates  s     zz---r/   c                d    U R                    Vs/ s H
  o"" U5      PM     nn[        U5      $ s  snf )zITrue when `element` begins a new semantic unit such as a section or page.)r   r   )r*   r   predsemantic_boundariess       r,   r   #PreChunker._is_in_new_semantic_unit  s7    
 :>9R9RS9RtG}9RS&'' Ts   -r   N)r   Iterable[Element]r   rI   )r   r   r   rI   r@   Iterator[PreChunk]r@   r   r   r   r   r@   r   )rA   rB   rC   rD   rE   r-   r   r   r   r   r   r   rF   rG   r/   r,   r   r     sV    , 6(60?6	6 6-6 . .(r/   r   c                  r    \ rS rSrSrSS jrSS jrSS jrSS jr\	SS j5       r
SS jr\	SS	 j5       rS
rg)r   i  aN  An element accumulator suitable for incrementally forming a pre-chunk.

Provides the trial method `.will_fit()` a pre-chunker can use to determine whether it should add
the next element in the element stream.

`.flush()` is used to build a PreChunk object from the accumulated elements. This method
returns an iterator that generates zero-or-one `PreChunk` object and is used like so:

    yield from builder.flush()

If no elements have been accumulated, no `PreChunk` instance is generated. Flushing the builder
clears the elements it contains so it is ready to build the next pre-chunk.
c                |    Xl         [        UR                  5      U l        / U l        SU l        / U l        SU l        g )N r   )r   r8   r   _separator_lenr   _overlap_prefix_text_segments	_text_lenr*   r   s     r,   r-   PreChunkBuilder.__init__  s:    
!$"5"56(* %')+r/   c                t   [        U R                  5      S:X  a%  [        U5      (       a  SU l        / U l        SU l        U R                  R                  U5        UR                  (       aN  U R                  R                  UR                  5        U =R
                  [        UR                  5      -  sl        gg)zAdd `element` to this section.r   r   N)r8   r   r   r   r   r   appendr;   )r*   r   s     r,   r   PreChunkBuilder.add_element  s     t~~!#(@(I(I#%D "$DDNg&<<&&w||4NNc',,//N r/   c              #     #    U R                   nU(       d  g[        XR                  U R                  5      nUR                  n[        U5      S:X  a  [        US   5      (       a  SnU R                  U5        Uv   g7f)zGenerate zero-or-one `PreChunk` object and clear the accumulator.

Suitable for use to emit a PreChunk when the maximum size has been reached or a semantic
boundary has been reached. Also to clear out a terminal pre-chunk at the end of an element
stream.
N   r   r   )r   PreChunkr   r   overlap_tailr8   r   _reset_state)r*   r   	pre_chunkoverlap_for_nexts       r,   r   PreChunkBuilder.flush  sq      >> X';';TZZH	 %11x=A":8A;"G"G!*+s   A;A=c                   [        U5      (       a  [        U R                  5      S:H  $ [        U R                  5      (       a  g[        U R                  5      S:X  a  gU R                  U R
                  R                  :  a  gU R
                  R                  (       a  U R
                  R                  R                  U R                  UR                  (       a  UR                  /O/ -   5      nU R
                  R                  U5      U R
                  R                  :*  $ U R                  [        UR                  =(       d    S5      :  + $ )a+  True when `element` can be added to this prechunk without violating its limits.

There are several limits:
- A `Table` element will never fit with any other element. It will only fit in an empty
  pre-chunk.
- No element will fit in a pre-chunk that already contains a `Table` element.
- A text-element will not fit in a pre-chunk that already exceeds the soft-max
  (aka. new_after_n_chars/new_after_n_tokens).
- A text-element will not fit when together with the elements already present it would
  exceed the hard-max (aka. max_characters/max_tokens).
r   FTr   )r   r8   r   r   _text_lengthr   r~   rc   r   joinr   r;   r   rd   _remaining_space)r*   r   new_texts      r,   r   PreChunkBuilder.will_fit  s     $G,,t~~&!++ *$..99 t~~!#tzz222 ::((zz0055##~2NH ::%%h/4::3F3FFF((3w||/Ar+BBBr/   c                    U R                   [        U R                  5      -  nU R                  R                  U R
                  -
  U-
  $ )zMMaximum text-length of an element that can be added without exceeding maxlen.)r   r8   r   r   rd   r   r*   separators_lens     r,   r    PreChunkBuilder._remaining_space?  s=     ,,s43F3F/GGzz""T^^3nDDr/   c                    Xl         U R                  R                  5         U(       a  U/O/ U l        [	        U5      U l        g)zMSet working-state values back to "empty", ready to accumulate next pre-chunk.N)r   r   clearr   r8   r   )r*   overlap_prefixs     r,   r   PreChunkBuilder._reset_stateF  s3    -2@~.b^,r/   c                l   U R                   R                  (       a\  U R                  (       d  gU R                   R                  R	                  U R                  5      nU R                   R                  U5      $ [        U R                  5      nU(       a  US-
  OSnU R                  X0R                  -  -   $ )a  Size of the text in this pre-chunk (in characters or tokens depending on mode).

This value represents the chunk-size that would result if this pre-chunk was flushed in its
current state. In particular, it does not include the length of a trailing separator (since
that would only appear if an additional element was added).

Not suitable for judging remaining space, use `.remaining_space` for that value.
r   r   )	r   rc   r   r   r   r   r8   r   r   )r*   r;   nseparator_counts       r,   r   PreChunkBuilder._text_lengthM  s     ::((&&::,,11$2E2EFD::%%d++ ##$#$!a%!~~3F3F!FGGr/   )r   r   r   r   r   r   Nr   rI   r@   r   )r   r   r@   r   r   r   r   )r   r>   r@   r   )rA   rB   rC   rD   rE   r-   r   r   r   propertyr   r   r   rF   rG   r/   r,   r   r     sN    
 0."CH E E- H Hr/   r   c                      \ rS rSrSr        SS jrSS jrSS jrSS jrSS jr	\
SS j5       rSS	 jr\
SS
 j5       rSrg)r   il  zYSequence of elements staged to form a single chunk.

This object is purposely immutable.
c                <    [        U5      U l        X l        X0l        g r&   )listr   r   r   )r*   r   r   r   s       r,   r-   PreChunk.__init__r  s     h-
r/   c                    [        U[        5      (       d  gU R                  UR                  :H  =(       a    U R                  UR                  :H  $ )NF)r   r   r   r   )r*   others     r,   __eq__PreChunk.__eq__y  s>    %**##u'<'<<bSXSbSbAbbr/   c                "   [        U R                  UR                  5      (       a  g[        U R                  5      U R                  R
                  :  a  g[        U R                  U5      R                  5      nX R                  R                  :*  $ )zRTrue when `pre_chunk` can be combined with this one without exceeding size limits.F)r   r   r8   _textr   rZ   combinerd   )r*   r   combined_lens      r,   can_combinePreChunk.can_combine~  sg    6t~~yGZGZ[[tzz?djjCCC
 4<<	2889zz2222r/   c                n    [        U R                  UR                  -   U R                  U R                  S9$ )z?Return new `PreChunk` that combines this and `other_pre_chunk`.)r   r   )r   r   r   r   )r*   other_pre_chunks     r,   r  PreChunk.combine  s3     NN_666//
 	
r/   c              #    #    [        U R                  5      S:X  a  [        U R                  S   [        5      (       an  U R                  R
                  (       a  U R                  S   v   g[        R                  U R                  S   U R                  U R                  5       Sh  vN   g[        R                  U R                  U R                  U R                  5       Sh  vN   g NC N7f)a4  Form this pre-chunk into one or more chunk elements maxlen or smaller.

When the total size of the pre-chunk will fit in the chunking window, a single chunk it
emitted. When this prechunk contains an oversized element (always isolated), it is split
into two or more chunks that each fit the chunking window.
r   r   N)r8   r   r   r   r   rn   _TableChunkeriter_chunksr   _Chunkerr  rV   s    r,   r  PreChunk.iter_chunks  s      t~~!#
4>>!3De(L(Lzz--nnQ''(44NN1%t';';TZZ    ++DNNDJJ

SSS	 Ts$   B$C.&C*'=C.$C,%C.,C.c                |    U R                   R                  nU(       a  U R                  U* S R                  5       $ S$ )a  The portion of this chunk's text to be repeated as a prefix in the next chunk.

This value is the empty-string ("") when either the `.overlap` length option is `0` or
`.overlap_all` is `False`. When there is a text value, it is stripped of both leading and
trailing whitespace.
Nr   )r   rt   r  strip)r*   rr   s     r,   r   PreChunk.overlap_tail  s6     **0007tzz7()$**,?R?r/   c              #    #    U R                   (       a  U R                   v   U R                   H  nUR                  (       d  M  [        UR                  5      (       d  M2  [	        U[
        5      (       a  UR                  v   MW  SR                  UR                  R                  5       R                  5       5      nU(       d  M  Uv   M     g7f)zGenerate overlap text and each element text segment in order.

Empty text segments are not included. CodeSnippet elements preserve their
original whitespace (including newlines) to maintain code formatting.
r   N)	r   r   r;   r8   r   r   r   r  r   )r*   r   r;   s      r,   _iter_text_segmentsPreChunk._iter_text_segments  s      &&&Avvv#aff++a--&&L88AFFLLN$8$8$:;Dt"
  s   >CC A!C	Cc                h    U R                   R                  R                  U R                  5       5      $ )zThe concatenated text of all elements in this pre-chunk, including any overlap.

        Whitespace is normalized to a single space. The text of each element is separated from
        that of the next by a blank line ("

").
        )r   r   r   r  rV   s    r,   r  PreChunk._text  s(     zz((--d.F.F.HIIr/   )r   r   r   N)r   r   r   r>   r   rI   r@   r   )r   r   r@   r   r   r   r@   r   )r  r   r@   r   )r@   z/Iterator[CompositeElement | Table | TableChunk]r   r@   zIterator[str])rA   rB   rC   rD   rE   r-   r   r  r  r  r   r   r  r  rF   rG   r/   r,   r   r   l  ss    
);>FU	c
3
T* @ @#$ J Jr/   r   c                      \ rS rSrSrSS jr\        SS j5       rSS jr\	SS j5       r
\	SS j5       r\	SS j5       r\	SS	 j5       r\	SS
 j5       rSrg)r  i  z{Forms chunks from a pre-chunk other than one containing only a `Table`.

Produces zero-or-more `CompositeElement` objects.
c                <    [        U5      U l        X l        X0l        g r&   )r   r   r  r   )r*   r   r;   r   s       r,   r-   _Chunker.__init__  s    h

r/   c                0    U " XU5      R                  5       $ )zForm zero or more chunks from `elements`.

One `CompositeElement` is produced when all `elements` will fit. Otherwise there is a
single `Text`-subtype element and chunks are formed by splitting.
_iter_chunks)rR   r   r;   r   s       r,   r  _Chunker.iter_chunks  s     84(5577r/   c              #    #    U R                   (       d  gU R                  R                  nU" U R                   5      u  p#[        X R                  S9v   U(       a)  U" U5      u  p#[        X R
                  S9v   U(       a  M(  gg7f)z)Form zero or more chunks from `elements`.Nr;   metadata)r  r   r   r   _consolidated_metadata_continuation_metadata)r*   r   s	remainders       r,   r  _Chunker._iter_chunks  sn      zz 

   TZZ(A0K0KLL  +LA"4O4OPP is   A?BBc                    SS jn[         R                  " [        5      nU R                   H1  nU" UR                  5       H  u  pEX$   R                  U5        M     M3     [        U5      $ )aL  Collection of all populated metadata values across elements.

The resulting dict has one key for each `ElementMetadata` field that had a non-None value in
at least one of the elements in this pre-chunk. The value of that key is a list of all those
populated values, in element order, for example:

    {
        "filename": ["sample.docx", "sample.docx"],
        "languages": [["lat"], ["lat", "eng"]]
        ...
    }

This preprocessing step provides the input for a specified consolidation strategy that will
resolve the list of values for each field to a single consolidated value.
c                D    S U R                   R                  5        5       $ )zM(field_name, value) pair for each non-None field in single `ElementMetadata`.c              3  6   #    U  H  u  pUc  M
  X4v   M     g 7fr&   rG   )r   
field_namevalues      r,   r   O_Chunker._all_metadata_values.<locals>.iter_populated_fields.<locals>.<genexpr>  s"      )F%J $#)Fs   	
)known_fieldsitems)r"  s    r,   iter_populated_fields<_Chunker._all_metadata_values.<locals>.iter_populated_fields  s#    )1)>)>)D)D)F r/   )r"  r   r@   Iterator[tuple[str, Any]])collectionsdefaultdictr   r   r"  r   dict)r*   r0  field_valuesr   r+  r,  s         r,   _all_metadata_values_Chunker._all_metadata_values  s_    $	 5@4K4KD4Q A%:1::%F!
(//6 &G   L!!r/   c                    [        S0 U R                  D6nU R                  R                  (       a  U R                  Ul        U$ )ac  Metadata applicable to this pre-chunk as a single chunk.

Formed by applying consolidation rules to all metadata fields across the elements of this
pre-chunk.

For the sake of consistency, the same rules are applied (for example, for dropping values)
to a single-element pre-chunk too, even though metadata for such a pre-chunk is already
"consolidated".
rG   )r   _meta_kwargsr   rg   _orig_elementsorig_elements)r*   consolidated_metadatas     r,   r#  _Chunker._consolidated_metadata$  s:     !0 D$2C2C D::++262E2E!/$$r/   c                T    [         R                   " U R                  5      nSUl        U$ )zMetadata applicable to the second and later text-split chunks of the pre-chunk.

The same metadata as the first text-split chunk but includes `.is_continuation = True`.
Unused for non-oversized pre-chunks since those are not subject to text-splitting.
T)copyr#  is_continuation)r*   continuation_metadatas     r,   r$  _Chunker._continuation_metadata4  s(     !%		$*E*E F04-$$r/   c                r   ^ ^^ [         m[         R                  " 5       mSUUU 4S jjn[        U" 5       5      $ )a  The consolidated metadata values as a dict suitable for constructing ElementMetadata.

This is where consolidation strategies are actually applied. The output is suitable for use
in constructing an `ElementMetadata` object like `ElementMetadata(**self._meta_kwargs)`.
c            	   3  H  >#    TR                   R                  5        H  u  pTR                  U 5      nUTR                  L a
  XS   4v   M/  UTR                  L a  U [        U[        S/ 5      5      4v   MZ  UTR                  L a<  U VVs0 s H  o3  H  oDS_M     M     nnnU [        UR                  5       5      4v   M  UTR                  L a  U SR                  S U 5       5      4v   M  UTR                  L a  M  [        S[        U 5       S35      e   gs  snnf 7f)zKGenerate (field-name, value) pairs for each field in consolidated metadata.r   z	list[Any]Nr   c              3  @   #    U  H  oR                  5       v   M     g 7fr&   )r  )r   vals     r,   r   B_Chunker._meta_kwargs.<locals>.iter_kwarg_pairs.<locals>.<genexpr>[  s     .Mfsyy{{fs   zmetadata field z& has no defined consolidation strategy)r7  r/  r\   FIRSTLIST_CONCATENATEsumr
   LIST_UNIQUEr   keysSTRING_CONCATENATEr   DROPNotImplementedErrorrepr)	r+  valuesstrategyval_listkeyordered_unique_keysCSfield_consolidation_strategiesr*   s	         r,   iter_kwarg_pairs/_Chunker._meta_kwargs.<locals>.iter_kwarg_pairsL  s    &*&?&?&E&E&G"
9==jIrxx'$Qi//!4!44$c&${B2G&HHH/EK*`VW_PS9W_3V'*`$d+>+C+C+E&FFF!6!66$chh.Mf.M&MMM( .)$z*:);;ab ' 'H +as   B	D"D!BD")r@   r2  )r   rX  r5  )r*   rY  rW  rX  s   ` @@r,   r:  _Chunker._meta_kwargsB  s4     #)>)])])_&	 	2 $&''r/   c                0   ^  U 4S jn[        U" 5       5      $ )zJThe `.metadata.orig_elements` value for chunks formed from this pre-chunk.c               3     >#    TR                    HK  n U R                  R                  c  U v   M   [        R                  " U 5      nS UR                  l        Uv   MM     g 7fr&   )r   r"  r<  r@  )r   orig_elementr*   s     r,   iter_orig_elements3_Chunker._orig_elements.<locals>.iter_orig_elementsk  sQ     ^^::++3G  $yy| 7;%%3"" $s   AA )r   )r*   r_  s   ` r,   r;  _Chunker._orig_elementsg  s    	# &())r/   )r   r   r  N)r   r   r;   r>   r   rI   r@   r   )r   r   r;   r>   r   rI   r@   Iterator[CompositeElement])r@   rb  )r@   zdict[str, list[Any]]r@   r   )r@   zdict[str, Any]r@   zlist[Element])rA   rB   rC   rD   rE   r-   r   r  r  r   r7  r#  r$  r:  r;  rF   rG   r/   r,   r  r    s    

 8(8038;J8	#8 8Q(  "  "D % % % % "( "(H * *r/   r  c                  
   \ rS rSrSrSS jr\        SS j5       rSS jr\	SS j5       r
\	SS j5       r\	SS j5       rSS	 jrSS
 jr S     SS jjr\SS j5       r\	SS j5       r\	SS j5       r\	SS j5       rSrg)r  i{  zResponsible for forming chunks, especially splits, from a single-table pre-chunk.

Table splitting is specialized because we recursively split on an even row, cell, text
boundary. This object encapsulate those details.
c                (    Xl         X l        X0l        g r&   )_tabler   r   )r*   tabler   r   s       r,   r-   _TableChunker.__init__  s    -
r/   c                0    U " XU5      R                  5       $ )LSplit this pre-chunk into `Table` or `TableChunk` objects maxlen or smaller.r  )rR   rh  r   r   s       r,   r  _TableChunker.iter_chunks  s    
 5$/<<>>r/   c              #    #    U R                   (       d  gU R                  R                  nU R                  R                  nU" U R                  5      nU R
                  (       a  U" U R
                  5      OSnX1::  aB  XA::  a=  U R                  nU R
                  =(       d    SUl        [        U R                  US9v   gU R                  R                  (       a  SOSnU R
                  (       a  U R                  R                  U:  a  U R                  5        Sh  vN   gU R                  5        Sh  vN   g N N7f)rk  Nr   r!     2   )_table_textr   rd   r   _text_with_overlap_html	_metadatatext_as_htmlr   rc   _iter_text_only_table_chunks _iter_text_and_html_table_chunks)r*   maxlenr   	text_size	html_sizer"  min_html_thresholds          r,   r  _TableChunker._iter_chunks  s       $$**$$D334	+/::GDJJ'1	9#6~~H$(JJ$6$H!T44xHH $(::#@#@RbzzTZZ003EE88::: 88:::	 ; 	;s$   DD?D;D?5D=6D?=D?c                B    U R                   =n(       d  gUR                  $ )zThe compactified HTML for this table when it has text-as-HTML.

The empty string when table-structure has not been captured, perhaps because
`infer_table_structure` was set `False` in the partitioning call.
r   )_html_tablehtml)r*   
html_tables     r,   rr  _TableChunker._html  s"     #...
.r/   c                0   U R                   R                  R                  =nc  gUR                  5       nU(       d  g [        R
                  " U5      $ ! [        [        4 a1    [        R                  " S[        U5      S:  a  USS S-   OU5         gf = f)zqThe `lxml` HTML element object for this table.

`None` when the `Table` element has no `.metadata.text_as_html`.
Nz^Could not parse text_as_html for table element; skipping HTML-based chunking. text_as_html: %sd   z...)rg  r"  rt  r  r   from_html_textr   r   r   warningr8   )r*   rt  s     r,   r}  _TableChunker._html_table  s     !KK00===LF#))+	++L99Z( 	NN$.1,.?#.ETc"U*<
 	s   A >BBc                    U R                   nUc  gSnUR                  5        H  nUR                  (       d    U$ US-  nM     U$ )zJNumber of contiguous leading rows that should be treated as table headers.r   r   )r}  	iter_rows	is_header)r*   r  r<   rows       r,   _leading_header_row_count'_TableChunker._leading_header_row_count  sQ     %%
'')C== QJE * r/   c              #  >  #    U R                   =nc  [        S5      eU R                  R                  (       a  U R                  OSn[        UU R                  US9nU R                  [
        R                  UU R                  US9UR                  S9 Sh  vN   g N7f)zSplit table into chunks where HTML corresponds exactly to text.

`.metadata.text_as_html` for each chunk is a parsable `<table>` HTML fragment.
Nz<this method is undefined for a table having no .text_as_htmlr   header_row_count)num_carried_over_header_rows)	r}  r   r   rk   r  _HtmlTableSplitter_make_table_chunksiter_subtablescarried_over_header_row_count)r*   r  r  splitters       r,   rv  ._TableChunker._iter_text_and_html_table_chunks  s     
 ***J3[\\=AZZ=\=\499bc%JJ-

 **--

!1 . 
 *2)O)O + 
 	
 	
s   BBBBc              #  ^   ^ #    SU 4S jjnT R                  U" 5       5       Sh  vN   g N7f)zSplit oversized text-only table (no text-as-html) into chunks.

`.metadata.text_as_html` is optional, not included when `infer_table_structure` is
`False`.
c               3     >#    TR                   n TR                  R                  nU (       a  U" U 5      u  p US 4v   U (       a  M  g g 7fr&   )rq  r   r   )text_remainderr   
chunk_textr*   s      r,   _iter_text_splitsE_TableChunker._iter_text_only_table_chunks.<locals>._iter_text_splits  sB     !44NJJ$$E -2>-B*
 $&& !.s   AAAN)r@   zIterator[tuple[str, None]])r  )r*   r  s   ` r,   ru  *_TableChunker._iter_text_only_table_chunks  s%     	' **+<+>???s   "-+-c              #  f  #    [        [        R                  " 5       5      n[        SU5      n[	        U5       Ht  u  nu  pgU R
                  nUb  Xxl        OSUl        US:  =(       d    SUl        US:  a  UOSUl        [        XhS9n	X9R                  l        XYR                  l        U	v   Mv     g7f)a  Form `TableChunk` objects from (text, html) pairs.

Handles `is_continuation` and chunk sequencing metadata (`table_id`, `chunk_index`)
so the original table can be reconstructed from its chunks. Carries
`num_carried_over_header_rows` so synthetic repeated header rows can be removed.
r   Nr!  )r>   uuiduuid4max	enumeraters  rt  rA  r  r   r"  table_idchunk_index)
r*   text_html_pairsr  r  carried_header_row_countr  r;   r~  r"  chunks
             r,   r   _TableChunker._make_table_chunks  s      tzz|$#&q*F#G )2?)C%K$~~H(,%(,%(3a'@DH$,7!O( 1 D<E&.NN#)4NN&K *Ds   B/B1c                x   [         n[        R                  " U R                  R                  5      nUR                  5       R                  5        VVs/ s H  u  p4XAR                  L d  M  UPM     nnnU H  n[        X#S5        M     U R                  R                  (       a  U R                  Ul        U$ s  snnf )a  The base `.metadata` value for chunks formed from this pre-chunk.

The term "base" here means that other metadata fields will be added, depending on the
chunk. In particular, `.metadata.text_as_html` will be different for each text-split chunk
and `.metadata.is_continuation` must be added for second-and-later text-split chunks.

Note this is a fresh copy of the metadata on each call since it will need to be mutated
differently for each chunk formed from this pre-chunk.
N)r   r@  deepcopyrg  r"  rX  r/  rO  setattrr   rg   r;  r<  )r*   rW  r"  r+  rS  drop_field_namess         r,   rs  _TableChunker._metadata&  s     #==!5!56 )+(I(I(K(Q(Q(S
(S$
77" (S 	 

 +JH$/ + ::++%)%8%8H"
s   B6*B6c                j    [         R                  " U R                  5      nSUR                  l        U/$ )a  The `.metadata.orig_elements` value for chunks formed from this pre-chunk.

Note this is not just the `Table` element, it must be adjusted to strip out any
`.metadata.orig_elements` value it may have when it is itself a chunk and not a direct
product of partitioning.
N)r@  r  rg  r"  r<  )r*   
orig_tables     r,   r;  _TableChunker._orig_elementsB  s,     ]]4;;/
,0
)|r/   c                    U R                   R                  (       d  gSR                  U R                   R                  R                  5       5      $ )zMThe text in this table, not including any overlap-prefix or extra whitespace.r   r   )rg  r;   r   r   rV   s    r,   rp  _TableChunker._table_textQ  s6     {{xx((..011r/   c                    U R                   nU R                  R                  (       d  SO#U R                  R                  R                  5       nU(       a  US-   U-   $ U$ )zCThe text for this chunk, including the overlap-prefix when present.r   r   )r   rg  r;   r  )r*   r   
table_texts      r,   rq   _TableChunker._text_with_overlapX  sL     --#{{//RT[[5E5E5K5K5M
5C~$z1SSr/   )r   r   rg  N)rh  r   r   r>   r   rI   r@   r   )rh  r   r   r>   r   rI   r@   Iterator[Table | TableChunk])r@   r  r   )r@   zHtmlTable | Noner   )r@   Iterator[TableChunk]r   )r  z Iterator[tuple[str, str | None]]r  r   r@   r  rc  rd  )rA   rB   rC   rD   rE   r-   r   r  r  r   rr  r}  r  rv  ru  r  r   rs  r;  rp  rq  rF   rG   r/   r,   r  r  {  s   
 ??+.?6E?	%? ?;@ 	 	  ,  
.@( -.9 '* 
	>  6   2 2 T Tr/   r  c                  4   \ rS rSrSrSSS jjr\ S       SS jj5       rSS jrSS jr	SS jr
\SS j5       r\SS	 j5       r\SS
 j5       r\SS j5       r\SS j5       r\SS j5       r\SS j5       rSS jrS S jr\S!S j5       r\S"S j5       rSrg)#r  if  a\  Produces (text, html) pairs for a `<table>` HtmlElement.

Each chunk contains a whole number of rows whenever possible. An oversized row is split on an
even cell boundary and a single cell that is by itself too big to fit in the chunking window
is divided by text-splitting.

The returned `html` value is always a parseable HTML `<table>` subtree.
c                >    Xl         X l        [        SU5      U l        g Nr   )_table_elementr   r  _header_row_count)r*   table_elementr   r  s       r,   r-   _HtmlTableSplitter.__init__p  s    +
!$Q(8!9r/   c                ,    U " XUS9R                  5       $ )zGenerate (text, html) pair for each split of this table pre-chunk.

Each split is on an even row boundary whenever possible, falling back to even cell and even
word boundaries when a row or cell is by itself oversized, respectively.
r  )_iter_subtables)rR   r  r   r  s       r,   r  !_HtmlTableSplitter.iter_subtablesu  s     =9IJZZ\\r/   c              #    #    Sn[        U R                  U5      U R                  R                  S9nU R                  R                  5        GH	  nUR                  U5      (       d\  UR                  5        H  u  pEU R                  XEU5      v   SnM     [        U R                  U5      U R                  R                  S9nUR                  U5      (       a  UR                  U5        M  U R                  X0R                  U5      S9 H  u  pEU R                  XEU5      v   SnM     [        U R                  U5      U R                  R                  S9nGM     UR                  5        H  u  pEU R                  XEU5      v   SnM     g7f)zGenerate (text, html) pairs containing as many whole rows as will fit in window.

Falls back to splitting rows into whole cells when a single row is by itself too big to
fit in the chunking window.
T)rw  r   Frw  N)_RowAccumulator_maxlenr   r   r  r  r   r   _prepend_repeated_headersadd_row_iter_row_splits)r*   is_first_chunkaccumr  r;   r~  s         r,   r  "_HtmlTableSplitter._iter_subtables  sK     t||N'CTZZM_M_`&&002C>>#&&"'++-JD88^TT%*N #0 (<<7ASAS ~~c""c""&"7"7LLQ_D`"7"aJD88^TT%*N #b (<<7ASAS! 3(  ++-JD00^LL"N (s   FF
c              #  d  #    [        US9nUR                  5        Hq  nUR                  U5      (       d  UR                  5        Sh  vN   UR                  U5      (       a  UR	                  U5        MZ  U R                  XBS9 Sh  vN   Ms     UR                  5        Sh  vN   g Na N# N	7f)zQSplit oversized row into (text, html) pairs containing as many cells as will fit.r  N)_CellAccumulator
iter_cellsr   r   add_cell_iter_cell_splits)r*   r  rw  r  cells        r,   r  #_HtmlTableSplitter._iter_row_splits  s      /NN$D>>$'' ;;=((~~d##t$11$1FFF % ;;=   )
 G s6   AB0B*	?B0B,	B0$B.%B0,B0.B0c              #    #    U R                   R                  (       a;  [        [        SUS-
  5      U R                   R                  R                  S5      S9nO[        [        SUS-
  5      S9n[        U5      nU" UR                  5      u  pVUSU S34v   U(       a  U" U5      u  pVUSU S34v   U(       a  M  g	g	7f)
zDSplit a single oversized cell into sub-sub-sub-table HTML fragments.r   
   r+   )ra   r+   !   )rb   z<table><tr><td>z</td></tr></table>N)r   rc   rI   r  rM   r\   r   r;   )r*   r  rw  r   r   r;   r&  s          r,   r  $_HtmlTableSplitter._iter_cell_splits  s     
 ::(( #q&2+.**,,00=D
 ##a"2EFDd#		*odV+=>>> #I.OD/$/ABBB is   B8B><B>c                F    SR                  S U R                   5       5      $ )z?Concatenated text for leading header rows identified by caller.r   c              3  R   #    U  H  oR                  5         H  o"v   M     M     g 7fr&   )iter_cell_texts)r   r  r;   s      r,   r   2_HtmlTableSplitter._header_text.<locals>.<genexpr>  s"     \(9FYFYF[dF[(9s   %')r   _header_rowsrV   s    r,   _header_text_HtmlTableSplitter._header_text  s     xx\(9(9\\\r/   c                    U R                   S::  a  g/ n[        U R                  R                  5       5       H'  u  p#X R                   :  a    OUR	                  U5        M)     [        U5      $ )zDLeading rows that should be repeated on continuation chunks, if any.r   rG   )r  r  r  r  r   r   )r*   rowsidxr  s       r,   r  _HtmlTableSplitter._header_rows  sb     !!Q& !$"5"5"?"?"ABHC,,,KK C T{r/   c                ~   ^  T R                   (       d  gSR                  U 4S jT R                    5       5      nSU S3$ )z;HTML for repeated header rows, preserving header semantics.r   c              3  F   >#    U  H  nTR                  U5      v   M     g 7fr&   )_as_header_row_htmlr   r  r*   s     r,   r   7_HtmlTableSplitter._header_rows_html.<locals>.<genexpr>  s!     WEVcD44S99EVs   !z<thead>z</thead>)r  r   )r*   	rows_htmls   ` r,   _header_rows_html$_HtmlTableSplitter._header_rows_html  s:       GGWTEVEVWW	8,,r/   c                R    U R                   (       a  [        U R                  5      $ S$ )zJHeader-row count prepended to each continuation chunk, or 0 when disabled.r   )_should_repeat_headersr8   r  rV   s    r,   r  0_HtmlTableSplitter.carried_over_header_row_count  s"     *.)D)Ds4$$%K!Kr/   c                x    U R                   (       d  gU R                  U R                  R                  S-   S-  :*  $ )zHTrue when header repetition is enabled and not pathologically expensive.Fr      )r  _max_header_row_lenr   rd   rV   s    r,   r  )_HtmlTableSplitter._should_repeat_headers  s7        ''DJJ,?,?!,C+IIIr/   c                f   ^  T R                   (       d  g[        U 4S jT R                    5       5      $ )z'Largest leading-header row text length.r   c              3     >#    U  H=  nTR                   R                  S R                  UR                  5       5      5      v   M?     g7f)r   N)r   r   r   r  r  s     r,   r   9_HtmlTableSplitter._max_header_row_len.<locals>.<genexpr>  s7     dRc34::%%chhs/B/B/D&EFFRcs   AA)r  r  rV   s   `r,   r  &_HtmlTableSplitter._max_header_row_len  s)       dRVRcRcdddr/   c                L    U R                   R                  U R                  5      $ )z/Size of repeated header text in chunking units.)r   r   r  rV   s    r,   _header_text_len#_HtmlTableSplitter._header_text_len  s     zz!!$"3"344r/   c                    U(       d  U R                   (       d  U R                  R                  $ [        SU R                  R                  U R                  -
  S-
  5      $ )z<Available size for non-header row content of the next chunk.r   )r  r   rd   r  r  )r*   r  s     r,   r  _HtmlTableSplitter._maxlen  sF    !<!<::&&& 1djj))D,A,AAAEFFr/   c                    U(       d  U R                   (       d  X4$ U R                  nU(       a  U(       a  U SU 3O
U=(       d    UnUR                  S5      R                  S5      nSU R                   U S3nXW4$ )z@Prepend repeated header rows to continuation chunk when enabled.r   <table></table>)r  r  removeprefixremovesuffixr  )r*   r;   r~  r  header_textr  
html_inner
chunk_htmls           r,   r  ,_HtmlTableSplitter._prepend_repeated_headers  sy    !<!<:''0;}AdV,;K^Z^
&&y1>>zJ
t556zl(K
%%r/   c                P   U R                   =(       d    U R                  n[        R                  U5      nUc0  U R                   (       a  [        R                  U R                  5      nUc  U R                  $ U H  n[	        USS5      S:X  d  M  SUl        M     [        U[        S9$ )zVSerialize `row` preserving source HTML while converting direct-child `<td>` to `<th>`.Ntagtdth)encoding)source_htmlr~  r  _parse_row_fragmentgetattrr  r   r>   )r  row_htmltrr  s       r,   r  &_HtmlTableSplitter._as_header_row_html  s     ??.chh33H=:#//#77AB:88ODtUD)T1  S))r/   c                     [        U 5      nUR                  S:X  a  U$ UR	                  S5      nU(       a  US   $ S$ ! [        [        4 a     gf = f)z>Parse `row_html` and return a `<tr>` element when recoverable.Nr  z.//trr   )r   r   r   r  xpath)r  parsedr  s      r,   r	  &_HtmlTableSplitter._parse_row_fragment#  s\    	(2F ::M||G$tAw(D( Z( 		s   > AA)r  r   r  Nr  )r  r   r   rI   r  r   )r  r   r   rI   r  r   r@   Iterator[TextAndHtml]r@   r  )r  r   rw  r   r@   r  )r  r   rw  r   r@   r  r   )r@   ztuple[HtmlRow, ...]r   r   )r  r   r@   r   )r;   r>   r~  r>   r  r   r@   r!   )r  r   r@   r>   )r  r>   )rA   rB   rC   rD   rE   r-   r   r  r  r  r  r   r  r  r  r  r  r  r  r  r  staticmethodr  r	  rF   rG   r/   r,   r  r  f  s!   :
 VW]%]-<]PS]	] ]#B! C0 ] ] 
 
 - - L L J J e e 5 5G
& * * ) )r/   r  c                  n    \ rS rSrSrSS jrSS jrSS jrSS jr\	SS j5       r
        SS jrS	rg
)r   i2  a  Provides a text-splitting function configured on construction.

    Text is split on the best-available separator, falling-back from the preferred separator
    through a sequence of alternate separators.

    - The separator is removed by splitting so only whitespace strings are suitable separators.
    - A "blank-line" ("

") is unlikely to occur in an element as it would have been used as an
      element boundary during partitioning.

    This is a *callable* object. Constructing it essentially produces a function:

        split = _TextSplitter(opts)
        fragment, remainder = split(s)

    This allows it to be configured with length-options etc. on construction and used throughout a
    chunking operation on a given element-stream.
    c                    Xl         g r&   r   r   s     r,   r-   _TextSplitter.__init__E  s    
r/   c                >   U R                   R                  nU R                   R                  (       a4  U R                   R                  U5      U::  a  US4$ U R	                  U5      $ [        U5      U::  a  US4$ U R                   HZ  u  p4U R                  X4U5      u  pVU(       a  [        U5      [        U5      :  a  M:  UR                  5       UR                  5       4s  $    USU R                  5       XU R                   R                  -
  S R                  5       4$ )a  Return pair of strings split from `s` on the best match of configured patterns.

The first string is the split, the second is the remainder of the string. The split string
will never be longer than `maxlen` (in characters or tokens depending on mode). The
separators are tried in order until a match is found. The last separator is "" which matches
between any two characters so there will always be a split.

The separator is removed and does not appear in the split or remainder.

An `s` that is already less than the maximum length is returned unchanged with no remainder.
This allows this function to be called repeatedly with the remainder until it is consumed
and returns a remainder of "".
r   N)r   rd   rc   r   _split_by_tokensr8   	_patterns_split_from_maxlenrstriplstriprr   )r*   r%  rw  psep_lenfragmentr&  s          r,   __call___TextSplitter.__call__H  s     $$ ::((zz!!!$."u((++ q6Vb5L..JA #'"9"9!a"HH y>SV+??$i&6&6&888 )  &z  "Atzz/A/A&A&C$D$K$K$MMMr/   c                    U R                   R                  nU R                   R                  nU R                   R                  nU R                   H  u  pV[        UR                  U5      5      nU H  nUR                  5       u  pUSU	 R                  5       nU" U5      U::  d  M6  U" U5      S:X  a  MD  XS R                  5       nUS:  a   U R                  X5      nUS-   U-   nX4s  s  $ X4s  s  $    M     S[        U5      nn[        US-   S5      nUU::  a,  UU-   S-  nU" USU 5      U::  a  UnUS-   nOUS-
  nUU::  a  M,  Un[        U[        US5      S5       H0  nU[        U5      :  d  M  UU   R                  5       (       d  M.  Un  O   USU R                  5       nU" U5      U:  a  UUS-   :  a  USU R                  5       nUnUUS R                  5       nUS:  a#  U(       a  U R                  X5      nUS-   U-   nX4$ X4$ )a  Split text `s` on a separator boundary while respecting token limits.

Tries each separator in order of preference, looking for the rightmost split position
that keeps the fragment under the token limit. Falls back to splitting on whitespace
boundaries if no separator works.
Nr   r   r   r  )r   rd   rr   r   r  r   finditerspanr  r  _get_token_overlap_tailr8   r  rangeisspace)r*   r%  rw  rr   r   pattern_matchesmatchmatch_start	match_endr!  raw_remaindertailoverlapped_remainderlowhighbest_posmid	split_posis                        r,   r  _TextSplitter._split_by_tokenst  s*    $$**$$**$$ ..JG 7++A./G ).&\k?1138$.x(A- $%jM$8$8$:M{#;;HN/3czM/I,'==#22 ! )0 s1vTw{A&Tk:!#Cq#w6)AgQw Tk 	xWa"5A3q6zadllnn	 6 Zi='')8v%)gk*A(|**,H I)*,,.Q;8//BD#'#:#= 11&&r/   c                p   U R                   R                  nU" U5      U::  a  UR                  5       $ S[        U5      pTXE:  a'  XE-   S-  nXS nU" U5      nX:  a  US-   nOUnXE:  a  M'  Un	U	[        U5      :  aD  X   R	                  5       (       d-  U	S-  n	U	[        U5      :  a  X   R	                  5       (       d  M-  U	[        U5      :  aD  X   R	                  5       (       a-  U	S-  n	U	[        U5      :  a  X   R	                  5       (       a  M-  U	[        U5      :  a  XS R                  5       $ XS $ )zExtract tail of text containing approximately `target_tokens` tokens.

Uses binary search to find the character position from which the tail contains
approximately the specified number of tokens. Adjusts to word boundaries to avoid
splitting words.
r   r  Nr   )r   r   r  r8   r*  r  )
r*   r;   target_tokensr   r4  r5  r7  r2  token_countposs
             r,   r(  %_TextSplitter._get_token_overlap_tail  s,    **$$ 4=M)::< s4yTj:!#C:D!$-K*Ag j CIodi&7&7&9&91HC CIodi&7&7&9&9CIo$)"3"3"5"51HC CIo$)"3"3"5"5 #d):$$&&Dzr/   c                R    U R                   R                  n[        S U 5       5      $ )a  Sequence of (pattern, len) pairs to match against.

Patterns appear in order of preference, those following are "fall-back" patterns to be used
if no match of a prior pattern is found.

NOTE these regexes search *from the end of the string*, which is what the "(?r)" bit
specifies. This is much more efficient than starting at the beginning of the string which
could result in hundreds of matches before the desired one.
c              3  j   #    U  H)  n[         R                  " S U 35      [        U5      4v   M+     g7f)z(?r)N)regexcompiler8   )r   seps     r,   r   *_TextSplitter._patterns.<locals>.<genexpr>  s(     S
emmd3%L13s8<
s   13)r   r   r   )r*   
separatorss     r,   r  _TextSplitter._patterns  s$     ZZ99
S
SSSr/   c                |   U R                   R                  U R                   R                  pTUR                  X5S-   XB-   S9nUc  SU4$ UR	                  5       u  pxSn	USU R                  5       n
X8S R                  5       nU[        U	5      ::  a  X4$ U[        U	5      -
  nX* S R                  5       nX-   U-   nX4$ )a  Return (split, remainder) pair split from `s` on the right-most match before `maxlen`.

Returns `"", s` if no suitable match was found. Also returns `"", s` if splitting on this
separator produces a split shorter than the required overlap (which would produce an
infinite loop).

`split` will never be longer than `maxlen` and there is no longer split available using
`pattern`.

The separator is removed and does not appear in either the split or remainder.
r   )r>  endposNr   r   )r   rd   rr   searchr'  r  r  r8   )r*   r+  r   r%  rw  rr   r.  r/  r0  	separatorr!  r1  tail_lenr2  r3  s                  r,   r   _TextSplitter._split_from_maxlen  s     **--tzz/A/A qk&:JK=q5L "'	 \k?))+*,,.c)n$** S^+	
#**,#/-?--r/   r  N)r   rI   )r%  r>   r@   tuple[str, str])r;   r>   r<  r   r@   r>   )r@   z*tuple[tuple[regex.Pattern[str], int], ...])r+  zregex.Pattern[str]r   r   r%  r>   r@   rN  )rA   rB   rC   rD   rE   r-   r"  r  r(  r   r  r  rF   rG   r/   r,   r   r   2  s[    $*NXD'L$L T T(.)(.47(.<?(.	(.r/   r   c                  ^    \ rS rSrSrSS jrSS jrSS jrSS jrSS jr	\
SS j5       rS	rg
)r  i  zIncrementally build `<table>` fragment cell-by-cell to maximally fill chunking window.

Accumulate cells until chunking window is filled, then generate the text and HTML for the
subtable composed of all those rows that fit in the window.
c                    Xl         / U l        g r&   )r  _cells)r*   rw  s     r,   r-   _CellAccumulator.__init__   s    &(r/   c                :    U R                   R                  U5        g)zPAdd `cell` to this accumulation. Caller is responsible for ensuring it will fit.N)rQ  r   r*   r  s     r,   r  _CellAccumulator.add_cell$  s    4 r/   c              #     #    U R                   (       d  gSR                  U R                  5       5      nSR                  S U R                    5       5      nSU S3nU R                   R                  5         X4v   g7f)zFGenerate zero-or-one (text, html) pairs for accumulated sub-sub-table.Nr   r   c              3  8   #    U  H  oR                   v   M     g 7fr&   r~  r   cs     r,   r   )_CellAccumulator.flush.<locals>.<genexpr>-  s     6+Q&&+r   z<table><tr>z</tr></table>)rQ  r   _iter_cell_textsr   )r*   r;   tds_strr~  s       r,   r   _CellAccumulator.flush(  sd     {{xx--/0''6$++66WI]3js   A:A<c                F    U R                   [        UR                  5      :  $ )zLTrue when `cell` will fit within remaining space left by accummulated cells.)r   r8   r;   rT  s     r,   r   _CellAccumulator.will_fit2  s    $$DII66r/   c              #  d   #    U R                    H  nUR                  =n(       d  M  Uv   M     g7f)zGenerate contents of each accumulated cell as a separate string.

A cell that is empty or contains only whitespace does not generate a string.
N)rQ  r;   )r*   r  r;   s      r,   r\  !_CellAccumulator._iter_cell_texts6  s,     
 KKD II%D%J  s   .0c                    [        U R                  5      nU R                  U-
  [        S U R                   5       5      -
  $ )zHNumber of characters remaining when text of accumulated cells is joined.c              3  L   #    U  H  n[        UR                  5      v   M     g 7fr&   )r8   r;   rY  s     r,   r   4_CellAccumulator._remaining_space.<locals>.<genexpr>F  s     2T13qvv;;s   "$)r8   rQ  r  rK  r   s     r,   r   !_CellAccumulator._remaining_space@  s5    
 T[[)||n,s2T2T/TTTr/   )rQ  r  N)rw  r   )r  r   r@   r   r  )r  r   r@   r   r  r   )rA   rB   rC   rD   rE   r-   r  r   r   r\  r   r   rF   rG   r/   r,   r  r    s6    )!7 U Ur/   r  c                  n    \ rS rSrSr\4SS jjrSS jrSS jrSS jr	SS jr
\SS j5       rSS	 jrS
rg)r  iI  zMaybe `SubtableAccumulator`.

Accumulate rows until chunking window is filled, then generate the text and HTML for the
subtable composed of all those rows that fit in the window.
c                8    Xl         X l        / U l        SU l        g r  )r  _measure_rows_row_text_len)r*   rw  r   s      r,   r-   _RowAccumulator.__init__P  s    $&
r/   c                    U R                   R                  U5        U =R                  U R                  U5      -  sl        g)zOAdd `row` to this accumulation. Caller is responsible for ensuring it will fit.N)rj  r   rk  _measured_row_text_lenr*   r  s     r,   r  _RowAccumulator.add_rowV  s0    

#d99#>>r/   c              #  
  #    U R                   (       d  gSR                  U R                  5       5      nSR                  S U R                    5       5      nSU S3nU R                   R                  5         SU l        X4v   g7f)zBGenerate zero-or-one (text, html) pairs for accumulated sub-table.Nr   r   c              3  8   #    U  H  oR                   v   M     g 7fr&   rX  )r   rs     r,   r   (_RowAccumulator.flush.<locals>.<genexpr>`  s     5*Q&&*r   r  r  r   )rj  r   r\  r   rk  )r*   r;   trs_strr~  s       r,   r   _RowAccumulator.flush[  sl     zzxx--/0''5$**55	*

js   BBc                >    U R                   U R                  U5      :  $ )zJTrue when `row` will fit within remaining space left by accummulated rows.)r   rn  ro  s     r,   r   _RowAccumulator.will_fitf  s    $$(C(CC(HHHr/   c              #  f   #    U R                    H  nUR                  5        Sh  vN   M     g N	7f)zGenerate contents of each row cell as a separate string.

A cell that is empty or contains only whitespace does not generate a string.
N)rj  r  )r*   rs  s     r,   r\   _RowAccumulator._iter_cell_textsj  s*     
 A((*** *s   #1/
1c                d    [        U R                  5      nU R                  U-
  U R                  -
  $ )z>Number of chunk-size units remaining for accumulated row text.)r8   rj  r  rk  r   s     r,   r    _RowAccumulator._remaining_spacer  s+    
 TZZ||n,t/A/AAAr/   c                ^    U R                  SR                  UR                  5       5      5      $ )z4Length of `row` text in configured chunk-size units.r   )ri  r   r  ro  s     r,   rn  &_RowAccumulator._measured_row_text_lenz  s#    }}SXXc&9&9&;<==r/   )r  ri  rk  rj  N)rw  r   r   zCallable[[str], int])r  r   r@   r   r  )r  r   r@   r   r  r   )r  r   r@   r   )rA   rB   rC   rD   rE   r8   r-   r  r   r   r\  r   r   rn  rF   rG   r/   r,   r  r  I  sC     EH ?
	I+ B B>r/   r  c                  ,    \ rS rSrSrSS jrSS jrSrg)	PreChunkCombineri  zDFilters pre-chunk stream to combine small pre-chunks where possible.c                    Xl         X l        g r&   )_pre_chunksr   )r*   
pre_chunksr   s      r,   r-   PreChunkCombiner.__init__  s    %
r/   c              #    #    [        U R                  5      nU R                   HB  nUR                  U5      (       d  UR	                  5        Sh  vN   UR                  U5        MD     UR	                  5        Sh  vN   g N2 N7f)zTGenerate pre-chunk objects, combining `PreChunk` objects when they'll fit in window.N)_PreChunkAccumulatorr   r  r   r   add_pre_chunk)r*   r  r   s      r,   iter_combined_pre_chunks)PreChunkCombiner.iter_combined_pre_chunks  sj     $TZZ0))I>>),, ;;=((	* * ;;=  	 ) 	!s$   ABB,B=B>BB)r   r  N)r  zIterable[PreChunk]r   rI   r   )rA   rB   rC   rD   rE   r-   r  rF   rG   r/   r,   r  r    s    N!r/   r  c                  @    \ rS rSrSrS	S jrS
S jrSS jrSS jrSr	g)r  i  a+  Accumulates, measures, and combines pre-chunks.

Used for combining pre-chunks for chunking strategies like "by-title" that can potentially
produce undersized chunks and offer the `combine_text_under_n_chars` option.

Provides `.add_pre_chunk()` allowing a pre-chunk to be added to the chunk and provides
monitoring properties `.remaining_space` and `.text_length` suitable for deciding whether to add
another pre-chunk.

`.flush()` is used to combine the accumulated pre-chunks into a single `PreChunk` object.
This method returns an interator that generates zero-or-one `PreChunk` objects and is used
like so:

    yield from accum.flush()

If no pre-chunks have been accumulated, no `PreChunk` is generated. Flushing the builder
clears the pre-chunks it contains so it is ready to accept the next pre-chunk.
c                    Xl         S U l        g r&   r   
_pre_chunkr   s     r,   r-   _PreChunkAccumulator.__init__  s    
+/r/   c                n    U R                   c  UU l         gU R                   R                  U5      U l         g)zPAdd a pre-chunk to the accumulator for possible combination with next pre-chunk.N)r  r  r*   r   s     r,   r  "_PreChunkAccumulator.add_pre_chunk  s0     0I 	6:oo6M6Mi6X 	r/   c              #  Z   #    U R                   (       d  gU R                   v   SU l         g7f)z~Generate accumulated pre-chunk as a single combined pre-chunk.

Does not generate a pre-chunk when none has been accumulated.
N)r  rV   s    r,   r   _PreChunkAccumulator.flush  s"      oos   )+c                T    U R                   c  gU R                   R                  U5      $ )a  True when there is room for `pre_chunk` in accumulator.

An empty accumulator always has room. Otherwise there is only room when `pre_chunk` can be
combined with any other pre-chunks in the accumulator without exceeding the combination
limits specified for the chunking run.
T)r  r  r  s     r,   r   _PreChunkAccumulator.will_fit  s&     ??"**955r/   r  Nr   )r   r   r@   r   r   r  )
rA   rB   rC   rD   rE   r-   r  r   r   rF   rG   r/   r,   r  r    s    &0
6r/   r  c                 $   ^^ SmSmSUU4S jjn U $ )at  Not a predicate itself, calling this returns a predicate that triggers on each new page.

The lifetime of the returned callable cannot extend beyond a single element-stream because it
stores current state (current page-number) that is particular to that element stream.

The returned predicate tracks the "current" page-number, starting at 1. An element with a
greater page number returns True, indicating the element starts a new page boundary, and
updates the enclosed page-number ready for the next transition.

An element with `page_number == None` or a page-number lower than the stored value is ignored
and returns False.
r   Tc                v   > U R                   R                  nT(       a  U=(       d    SmSmgUc  gUT:X  a  gUmg)Nr   FT)r"  page_number)r   r  current_page_numberis_firsts     r,   page_number_incremented0is_on_next_page.<locals>.page_number_incremented  sP     &&22
 "-"2H
 -- *r/   r   rG   )r  r  r  s    @@r,   is_on_next_pager    s"      !H 6 #"r/   c                "    [        U [        5      $ )z:True when `element` is a `Title` element, False otherwise.)r   r   r   s    r,   is_titler    s    gu%%r/   r   )r   r   r@   r   )r   r   r   r   r@   r   )r@   r    )ArE   
__future__r   r3  r@  r  	functoolsr   typingr   r   r   r   r	   r
   rB  
lxml.etreer   r   	lxml.htmlr   typing_extensionsr   r   unstructured.common.html_tabler   r   r   unstructured.documents.elementsr   r   r   r   r   r   r   r   unstructured.loggerr   r   __annotations__r   r   r    r   r>   r!   r#   rI   r   r   r   r   r   r   r  r  r  r   r  r  r  r  r  r  rG   r/   r,   <module>r     sq   F "    % G G  , ) - G G	 	 	 '  #  " "& $ %
  (	48 9 8 XsCxY (/ />P Pv&>

Y

Y
Y 

Y$H( H(VIH IHbcJ cJVa* a*HcT cTVI) I)Xd. d.N-U -U`3> 3>v! !*66 66X+#\&r/   