
    j]                    .   d Z ddlmZ ddlZddlZddlZddlmZmZm	Z	m
Z
mZ erddlmZmZmZmZmZ ddlmZ  eh d      Zd	d	dd
	 	 	 	 	 	 	 	 	 d#dZd$dZd%dZ	 	 	 	 d&dZ	 	 	 	 d'dZdd	 	 	 	 	 	 	 	 	 	 	 	 	 d(dZd)dZd*dZd+dZd,dZd-dZ d.dZ!	 	 	 	 d/dZ" ejF                  d      Z$ ejF                  d      Z% ejF                  dejL                        Z' ejF                  d      Z( ejF                  d      Z)d0dZ*d1d Z+d2d!Z,d3d"Z-y)4u   LLM input helper for Azure AI Content Understanding.

Provides ``to_llm_input`` — a single public function that converts
``AnalysisResult`` objects into LLM-friendly text (YAML front matter
+ markdown).
    )annotationsN)AnyDictListOptionalTYPE_CHECKING   )AnalysisContentAnalysisResultContentFieldDocumentContentDocumentPage)ODataV4Format>   pagesfieldscategory	timeRangecontentTyperai_warningsT)include_fieldsinclude_markdownmetadatac          
       
 ddl m} t        | |      s!t        dt	        |       j
                         t        |       | j                  syt        | j                        }|syddl m	
 t        
fd|D              }g }|D ]*  }t        || ||||dkD        }	|	s|j                  |	       , dj                  |      S )	a	  Convert a Content Understanding analysis result into LLM-friendly text.

    Produces a formatted text string from the analysis result,
    suitable for injecting into an LLM prompt, storing in a vector
    database, or passing as tool output.

    For single-content results (documents, images), the output is a
    flat text block. For multi-segment results (video, audio), each
    segment is rendered with its time range. For document
    classification results (parent with nested segments), the
    helper automatically expands the parent into per-segment blocks
    with category labels and markdown slices.

    :param result: The ``AnalysisResult`` from a Content Understanding analyze operation.
    :type result: ~azure.ai.contentunderstanding.models.AnalysisResult
    :keyword include_fields: Whether to include structured fields in the
        output. Defaults to True. Set to False for markdown-only
        output (smaller token footprint, no structured data).
    :paramtype include_fields: bool
    :keyword include_markdown: Whether to include markdown content in the
        output. Defaults to True. Set to False for fields-only
        output.
    :paramtype include_markdown: bool
    :keyword metadata: Optional dict of user-supplied key-value pairs to
        include in the YAML front matter. Common keys include
        ``"source"`` (filename), ``"department"``,
        ``"batch_id"``, etc. Metadata keys are placed after
        ``contentType`` and before auto-detected keys
        (``timeRange``, ``category``, ``pages``). Metadata keys must not
        conflict with helper-generated front matter keys.
    :paramtype metadata: dict[str, Any] or None
    :returns: A formatted text string with YAML front matter followed
        by markdown content.
    :rtype: str
    :raises TypeError: If *result* is not an ``AnalysisResult``.
    :raises ValueError: If *metadata* contains a reserved front matter key.

    Example::

        from azure.ai.contentunderstanding import (
            ContentUnderstandingClient,
            to_llm_input,
        )
        from azure.identity import DefaultAzureCredential

        client = ContentUnderstandingClient(endpoint, DefaultAzureCredential())
        poller = client.begin_analyze(
            analyzer_id="prebuilt-invoice",
            inputs=[{"source": {"kind": "url", "url": url}}],
        )
        result = poller.result()
        text = to_llm_input(result)
    r	   )r   zExpected AnalysisResult, got  )AudioVisualContentc              3  <   K   | ]  }t        |      sd   yw)r	   N)
isinstance).0c_AVs     g/root/aria/tools/markitdown-venv/lib/python3.12/site-packages/azure/ai/contentunderstanding/_helpers.py	<genexpr>zto_llm_input.<locals>.<genexpr>|   s     =*Q*<1=s   )r   r   r   is_multi_segmentz	

*****

)modelsr   r   	TypeErrortype__name___validate_metadatacontents_get_renderable_contentsr   sum_render_content_blockappendjoin)resultr   r   r   _AnalysisResultr)   av_countblockscontentblockr    s             @r!   to_llm_inputr5   0   s    x :fo.7V8M8M7NOPPx ??'8H1=h==HF 
!%)-&l
 MM% 
! ''    c                    | syt        t        |       j                  t                    }|r dj	                  |      }t        d| d      y)zValidate user-supplied front matter metadata.

    :param metadata: Optional user-supplied metadata.
    :type metadata: dict[str, Any] or None
    :raises ValueError: If metadata contains helper-generated front matter keys.
    N, z0metadata contains reserved front matter key(s): zJ. Use custom keys such as 'source', 'documentId', or 'department' instead.)sortedsetintersection_RESERVED_METADATA_KEYSr.   
ValueError)r   reservedkeyss      r!   r(   r(      s[     c(m001HIJHyy">tf EW W
 	
 r6   c                `    i }| j                         D ]  \  }}t        |      }||||<    |S )a  Flatten CU ContentField objects into plain Python dicts.

    Recursively resolves ObjectField and ArrayField values using each
    field's ``.value`` convenience property.

    :param fields: The ``fields`` dict from an ``AnalysisContent`` object.
    :type fields: dict[str, ~azure.ai.contentunderstanding.models.ContentField]
    :returns: A plain dict mapping field names to their resolved values.
    :rtype: dict[str, Any]
    )items_resolve_field_value)r   resolvednamefieldvals        r!   _resolve_fieldsrG      sA      "H||~ !e"5)? HTN! Or6   c                   ddl m}m} t        | |      r| j                  }|rt        |      S dS t        | |      r=| j                  }|r.g }|D ]!  }t        |      }||j                  |       # |xs dS y| j                  }|yt        |t        j                  t        j                  f      r|j                         S |S )zResolve a single ContentField to a plain Python value.

    :param field: The content field to resolve.
    :type field: ~azure.ai.contentunderstanding.models.ContentField
    :returns: The resolved plain Python value.
    :rtype: Any
    r	   )
ArrayFieldObjectFieldN)r$   rI   rJ   r   value_objectrG   value_arrayrB   r-   valuedatetimedatetime	isoformat)	rE   rI   rJ   objarrrA   itemrC   rF   s	            r!   rB   rB      s     0%%  '*s#44%$E +/5'LL*+
 =D  ++C
{ #x}}56}}Jr6   c           	     *   ddl m t               }| D ]D  }t        |      s|j                  s|j
                  s*|j                  |j
                         F g }d}| D ]  }t        |      rs|j                  rf|j                  sYd}|j
                  xs d}|j                  D ]5  }|j                  r| d|j                   nd}|r||v r*d}|j                  r|j                  rt        |j                  t              r|j                  j                  dd	      nt        |j                  dd	      }	t        |j                  t              r|j                  j                  d
d	      nt        |j                  d
d	      }
|j                  |	|	|
z    } |j                  |j                  |j                   ||j                        }|j#                  |       8 |j#                  |        |rdfd}|j%                  |       |S )ud  Filter contents for rendering, handling classification hierarchies.

    A "parent" content has ``segments`` but no ``category``. When
    detected, the parent is expanded — each segment becomes a
    synthetic ``DocumentContent`` with its category, page range,
    and the corresponding markdown slice extracted via the segment's
    ``span``.

    When a segment has a corresponding routed top-level content
    (linked by ``path`` = ``{parent_path}/{segment_id}``), the
    top-level item is used instead of the synthetic expansion to
    preserve extracted fields and avoid duplication.

    :param contents: The list of analysis contents to filter.
    :type contents: list[~azure.ai.contentunderstanding.models.AnalysisContent]
    :returns: The filtered list of renderable contents.
    :rtype: list[~azure.ai.contentunderstanding.models.AnalysisContent]
    r	   r   FTr   /Noffsetr   length)	mime_typestart_page_numberend_page_numbermarkdownr   c                N    t        |       r| j                  | j                  S yNr   )r   r[   )r   r   s    r!   	_sort_keyz+_get_renderable_contents.<locals>._sort_key#  s'    !_-!2E2E2Q***r6   key)r   'AnalysisContent'returnint)r$   r   r:   r   r   pathaddsegments
segment_idr]   spandictgetgetattrrZ   r[   r\   r-   sort)r)   routed_pathsr   r/   expanded_classificationparent_pathsegseg_pathmdrX   rY   childr`   r   s                @r!   r*   r*      s   * ( L %a)ajjQVVQVV$% ')F# a)ajj&*#&&,BK zz %@Ck]!CNN+;<TXL 8::#((:DSXXt:TSXX\\(A6ZabebjbjltvwZxF:DSXXt:TSXX\\(A6ZabebjbjltvwZxFFVf_=B'kk&)&;&;$'$7$7 \\ e$!%$ MM!14 	
 		"Mr6   F)r#   c                  ddl m}m} i }| j                  xs d|d<   |r|j	                         D ]
  \  }	}
|
||	<    t        | |      r=|r;| j                  /| j                  #t        | j                  | j                        |d<   | j                  r| j                  |d<   t        |       }|||d<   |r(| j                  rt        | j                        }|r||d<   |j                  rt        |j                        }|r||d	<   t        |      }|r8| j                   r,| j                   }t        | |      rt#        | |      }|d
z   |z   S |S )a  Render a single content item as front matter + body.

    :param content: The content item to render.
    :type content: ~azure.ai.contentunderstanding.models.AnalysisContent
    :param result: The full analysis result (used for warnings).
    :type result: ~azure.ai.contentunderstanding.models.AnalysisResult
    :keyword bool include_fields: Whether to include extracted fields.
    :keyword bool include_markdown: Whether to include markdown body.
    :keyword metadata: Optional user-provided metadata dict.
    :paramtype metadata: dict[str, Any] or None
    :keyword bool is_multi_segment: Whether this is part of a multi-segment result.
    :returns: The rendered string with YAML front matter and optional body.
    :rtype: str
    r	   )r   r   unknownr   r   r   r   r   r   
)r$   r   r   kindrA   r   start_time_msend_time_ms_format_time_ranger   _format_pagesr   rG   warnings_format_warnings_build_front_matterr]   _add_page_markers)r3   r/   r   r   r   r#   r   r   fmrb   rM   	pages_strrC   warnings_listfront_matterrt   s                   r!   r,   r,   -  s]   . < B  1	B} "..* 	JCBsG	 '-.3C  ,1D1D1P0%%w':':B{O
  )): g&I7 '.."7>>2#BxL (9!.B~ 'r*LG,,g/"7B/Bd"R''r6   c                j    | j                   rt        || j                         }||ur|S t        ||       S )aC  Add ``<!-- page N -->`` markers to document markdown.

    :param content: The document content with page information.
    :type content: ~azure.ai.contentunderstanding.models.DocumentContent
    :param str markdown: The markdown text to annotate.
    :returns: The markdown with page markers inserted.
    :rtype: str
    )r   _page_markers_from_spans_page_markers_from_breaks)r3   r]   r/   s      r!   r   r   }  s5     }})(GMMB!M$Xw77r6   c                   g }|D ]C  }|j                   s|j                  |j                   d   j                  |j                  f       E |s| S |j	                  d        t        j                  dd|       }t        j                  d      }g |j                  |       D ]I  }d}|j                         |j                         z
  |z
  }j                  |j                         |f       K dfd}	g }
d}|D ]8  \  }} |	|      }|
j                  |||        |
j                  d| d	       |}: |
j                  ||d
        dj                  |
      S )aD  Insert page markers using ``pages[].spans`` offsets.

    :param str markdown: The markdown text.
    :param pages: The list of document pages with span info.
    :type pages: list[~azure.ai.contentunderstanding.models.DocumentPage]
    :returns: The markdown with page markers inserted at span offsets.
    :rtype: str
    r   c                    | d   S r_    )ms    r!   <lambda>z*_page_markers_from_spans.<locals>.<lambda>  s
    qt r6   ra   \n*<!-- PageBreak -->\n*

   c                <    d}D ]  \  }}| |kD  s||z  } | |z
  S r_   r   )origtotalposdeltashiftss       r!   _adjusted_offsetz2_page_markers_from_spans.<locals>._adjusted_offset  s7      	JCcz	 e|r6   
<!-- page  -->

Nr   )r   re   rd   re   )spansr-   rX   page_numberrn   resubcompilefinditerendstartr.   )r]   r   markerspagecleanedbreak_patternr   replacement_lenr   r   partsprevrX   page_numadjr   s                  @r!   r   r     sY    G E::NNDJJqM00$2B2BCDE LL^L$ ff0&(CG JJ:;MF##H- *!'')#o5qwwy%()*
 ED# v&WT#&'z(845	
 
LL 775>r6   c                   d}|j                   |j                   }t        j                  d|       }g }t        |      D ]4  \  }}||z   }|j	                         }|s|j                  d| d|        6 dj                  |      S )aN  Fall back to splitting on ``<!-- PageBreak -->`` markers.

    :param str markdown: The markdown text.
    :param content: The document content with page number info.
    :type content: ~azure.ai.contentunderstanding.models.DocumentContent
    :returns: The markdown with page markers replacing PageBreak markers.
    :rtype: str
    r	   r   r   r   r   )r[   r   split	enumeratestripr-   r.   )	r]   r3   
start_pagechunksr   ichunkr   texts	            r!   r   r     s     J  ,..
XX18<FEf% @5>{{}LL:hZxv>?	@
 ;;ur6   c                0    dd} ||        d ||       S )u   Format millisecond timestamps as ``MM:SS – MM:SS``.

    :param int start_ms: Start time in milliseconds.
    :param int end_ms: End time in milliseconds.
    :returns: The formatted time range string.
    :rtype: str
    c                *    | dz  }|dz  dd|dz  dS )Ni  <   02d:r   )mstotal_ss     r!   _fmtz _format_time_range.<locals>._fmt  s*    *R-$Agl3%788r6   u    – )r   re   rd   strr   )start_msend_msr   s      r!   r|   r|     s#    9 8nXd6l^44r6   c                    ddl m} t        | |      sy| j                  r)t	        d | j                  D              }|rt        |      S | j                  }| j                  }||y||k(  r|S | d| S )a  Return a pages value: int for single page, string for range, or *None*.

    Uses the actual ``pages`` list when available to produce an accurate
    representation (e.g. ``"2-3, 5"`` instead of ``"2-5"`` when page 4 is
    absent).  Falls back to ``start_page_number`` / ``end_page_number``.

    :param content: The analysis content to extract page info from.
    :type content: ~azure.ai.contentunderstanding.models.AnalysisContent
    r	   rV   Nc              3  N   K   | ]  }|j                   |j                     y w)N)r   )r   ps     r!   r"   z _format_pages.<locals>.<genexpr>  s     Xamm>WammXs   %%-)r$   r   r   r   r9   _compress_page_numbersr[   r\   )r3   r   numsr   r   s        r!   r}   r}     s     (g/ }}XW]]XX)$// %%E

!
!C}|WAcUr6   c                2   | syt        |       dk(  r| d   S g }| d   }| d   }| dd D ]7  }||dz   k(  r|}|j                  ||k(  rt        |      n| d|        |}|}9 |j                  ||k(  rt        |      n| d|        dj                  |      S )zCompress a sorted list of page numbers into a compact string.

    Examples: ``[1] -> 1``, ``[1,2,3] -> "1-3"``, ``[2,3,5] -> "2-3, 5"``.

    :param nums: Sorted list of page numbers.
    :type nums: list[int]
    Nr	   r   r   r8   )lenr-   r   r.   )r   rangesr   r   ns        r!   r   r     s     
4yA~AwFGE7D!"X q=DMM#e*eWAdV;LMED MM#e*eWAdV3DE99Vr6   c                    g }| D ]l  }i }t        |dd      r|j                  |d<   t        |dd      r|j                  |d<   t        |dd      r|j                  |d<   |s\|j	                  |       n |S )a4  Convert ODataV4Format warnings to plain dicts for YAML output.

    :param warnings: The list of warnings to convert.
    :type warnings: list[~azure.ai.contentunderstanding.models.ODataV4Format]
    :returns: A list of warning dicts with code, message, and target keys.
    :rtype: list[dict[str, str]]
    codeNmessagetarget)rm   r   r   r   r-   )r~   rA   wentrys       r!   r   r   &  s     #%E 	  "1fd#FFE&M1i& yyE)1h%hhE(OLL	  Lr6   z)^[\-\?\:\,\[\]\{\}\#\&\*\!\|\>\'\"\%\@\`]z[:\#] |[\n\r]z!^(true|false|yes|no|on|off|null)$z)^[+\-]?(\d+\.?\d*|\.\d+)([eE][+\-]?\d+)?$z^\d{4}-\d{2}-\d{2}c                ^   | yt        | t              r| rdS dS t        | t              rt        |       S t        | t              rMt        j                  |       st        |       S | t        |       k(  rt        t        |             S t        |       S t        |       }| xsq t        j                  |      xsZ t        j                  |      xsC t        j                  |      xs, t        j                  |      xs t        j                  |      }|rd|j                  dd      z   dz   S |S )zSerialize a scalar to its YAML text form.

    :param value: The value to serialize.
    :type value: Any
    :returns: The YAML-formatted string.
    :rtype: str
    nulltruefalse'z'')r   boolre   r   floatmathisfinite
_YAML_BOOLmatch_YAML_NUMBER
_YAML_DATE_YAML_SPECIAL_STARTsearch_YAML_SPECIAL_INSIDEreplace)rM   sneeds_quotes      r!   _yaml_scalarr   I  s    }%v+G+%5z%}}U#u:"'3u:"5s3u:E3u:EE
A 	*A	*a 	* A	* %%a(		*
  &&q)  2=C!))C&&,C!Cr6   c                h    dg}t        || d       |j                  d       dj                  |      S )zBuild a ``---`` delimited YAML front matter string.

    :param data: The data to serialize as YAML front matter.
    :type data: dict[str, Any]
    :returns: The YAML front matter string.
    :rtype: str
    z---r   )indentrx   )_emit_mappingr-   r.   )dataliness     r!   r   r   h  s2     wE%a(	LL99Ur6   c           	     ~   d|z  }|j                         D ]  \  }}|	t        |      }t        |t              r*|s'| j	                  | | d       t        | ||dz          Nt        |t              r'|sa| j	                  | | d       t        | ||       | j	                  | | dt        |               y)zEmit a YAML mapping (block style).

    :param lines: The list of output lines to append to.
    :type lines: list[str]
    :param mapping: The mapping to emit.
    :type mapping: dict[str, Any]
    :param int indent: The current indentation level.
      Nr   r	   : )rA   r   r   rk   r-   r   list_emit_sequence)r   mappingr   prefixrb   rM   safe_keys          r!   r   r   v  s     F]Fmmo G
U=$eT"LLF8H:Q/0%
3t$LLF8H:Q/05%0LLF8H:RU0C/DEFGr6   c           
        d|z  }|D ]  }t        |t              rd}|j                         D ]  \  }}|	|r| dn| d}t        |      }	t        |t              r)|r'| j	                  | |	 d       t        | ||dz          nZt        |t              r)|r'| j	                  | |	 d       t        | ||dz          n!| j	                  | |	 dt        |              d} | j	                  | dt        |               y)	a  Emit a YAML sequence (block style, compact notation).

    :param lines: The list of output lines to append to.
    :type lines: list[str]
    :param sequence: The sequence to emit.
    :type sequence: list[Any]
    :param int indent: The current indentation level.
    r   TNz- r   r   r   F)r   rk   rA   r   r-   r   r   r   )
r   sequencer   r   rT   firstkvtagsafe_ks
             r!   r   r     s    F]F <dD!E

 19',mVHB-%aa&1LLC5!23!%FQJ74(QLLC5!23"5!VaZ8LLC5<?2C!DE LLF82l4&8%9:;%<r6   )
r/   'AnalysisResult'r   r   r   r   r   Optional[Dict[str, Any]]rd   r   )r   r   rd   None)r   zDict[str, 'ContentField']rd   Dict[str, Any])rE   z'ContentField'rd   r   )r)   'List[AnalysisContent]'rd   r   )r3   rc   r/   r   r   r   r   r   r   r   r#   r   rd   r   )r3   'DocumentContent'r]   r   rd   r   )r]   r   r   z'List[DocumentPage]'rd   r   )r]   r   r3   r   rd   r   )r   re   r   re   rd   r   )r3   rc   rd   r   )r   z'List[int]'rd   r   )r~   z'List[ODataV4Format]'rd   zList[Dict[str, str]])rM   r   rd   r   )r   r   rd   r   )r   	List[str]r   r   r   re   rd   r   )r   r   r   z	List[Any]r   re   rd   r   ).__doc__
__future__r   rN   r   r   typingr   r   r   r   r   r$   r
   r   r   r   r   azure.core.exceptionsr   	frozensetr<   r5   r(   rG   rB   r*   r,   r   r   r   r|   r}   r   r   r   r   r   
IGNORECASEr   r   r   r   r   r   r   r   r6   r!   <module>r     s   #   	 ; ;  4 $	 (  !)-[([( [( 	[(
 '[( 	[(|
0&%%%ZD%DD\ #HHH 	H
 H 'H H 	H`8 0f85 >6#8 !bjj!MN !rzz"23 RZZ<bmmL
rzzFGRZZ-.
D>G8<r6   