ó
    À‘´j¹â  ã                  óP  • S r SSKJr  SSKrSSK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Jr  SSKJr  SSKJrJrJrJrJr  SSKJr  SS	KJr  SS
KJrJ r    " S S\5      r!\RD                  " S\RF                  5      r$ " S S\5      r% " S S\5      r&\RD                  " S5      r'\RD                  " S5      r( " S S\5      r) " S S\5      r* " S S\5      r+Sr,Sr-Sr.Sr/\ " S S\5      5       r0 " S  S!5      r1 " S" S#5      r2S)S$ jr3 " S% S&5      r4\S'   r5S*S+S( jjr6g),uO  
LLMClient Protocol â€” the structured-extraction seam for table structure and
document heading review.

The Protocol declares four calls. Each is async, with a `_sync` sibling for
callers without an event loop:

- `count_tables(image_path)` â€” returns the integer count of text-based
  tables visible in an image. The validation hook (`core.validate`) uses it
  to surface Camelot-vs-LLM mismatches. The Set-of-Mark split probe
  (`core/extractors/set_of_mark/split.py`) counts the tables inside a cropped
  region to decide whether to split it.
- `correct_structure(image_path, markdown)` â€” structural correction step of
  the Camelot+LLM pipeline (`core/extractors/camelot/llm/pipeline.py`).
  Takes a Camelot-extracted markdown table and the page image, returns a
  corrected `LLMTableCorrection`. The LLM is **forbidden from changing
  numeric values** â€” that contract is enforced by the system prompt;
  Camelot remains the source of truth for cell contents.
- `vet_structure(image_png, markdown, region_text, page_text)` â€” grounded
  structure vetting. The cropped table image is the structural arbiter, and
  Camelot and the text layer are the value source. Called through
  `core/extractors/camelot/correspondence/correction.py`, which both the
  correspondence and the Set-of-Mark extractors use.
- `review_headings(nominated)` â€” judges which repeated document headings are
  page decoration. Called by `core/fusion/heading_review.py`.

Backend selection is via `QUBER_LLM_BACKEND` env var (or
`get_llm_client(backend=...)`):

- `api` â€” `PydanticAIClient`. Uses `pydantic-ai` with an Anthropic model.
  The default. It authenticates with the OAuth subscription token when one
  is set and with the API key otherwise.
- `cli` â€” `ClaudeCLIClient`. Shells out to the `claude` binary; uses the
  developer's subscription quota. Used only when selected.
- `mock` â€” `MockLLMClient`. Returns canned responses; for tests.

`agents/factory.py` (multi-provider pydantic-ai) is a separate pattern. It
builds the agents of the processors in `quber/processors/`, such as table
inference with its executive summary.
é    )ÚannotationsN)ÚPath)ÚAnyÚDictÚListÚLiteralÚOptionalÚProtocolÚruntime_checkable)Úlogger)Ú	BaseModelÚFieldÚValidationErrorÚfield_validatorÚmodel_validator)Úsupports_sampling_temperature)Úusage_metadata_from)ÚDEFAULT_LLM_MODELÚget_settingsc                  óº   • \ rS rSr% Sr\" SS9rS\S'   \" SSS	9rS
\S'   \" SSS	9r	S
\S'   \" SS9r
S\S'   \" \SS9rS\S'   \" SS5      \SS j5       5       rSrg)Ú	CellMergeé<   uê  One corrected cell built from one or more source (Camelot) cells â€” a
rejoined split symbol, a flattened multi-row header, a replicated band or
spanning label, or a label extended with its footnote marker. Reported by
the correction agent so the cell's geometry can be recovered from its
source cells, and addressed by its exact position in the corrected table
(result text alone is ambiguous when two cells share the same text, e.g.
an identical total in the Basic and Diluted EPS rows).
z>The combined cell text, exactly as in your corrected markdown.©ÚdescriptionÚstrÚresultr   zp0-based row of this cell in YOUR corrected markdown table: the header row is row 0, the first body row is row 1.©Úger   ÚintÚrowzU0-based column of this cell in your corrected markdown table, counting from the left.ÚcolziThe Camelot cells you joined to make it, each copied exactly from the Camelot markdown, in reading order.ú	List[str]Úsourcesz×The address of each Camelot cell you joined, copied from the tag printed next to that cell's text in the Camelot markdown ('[B3] 1,637' -> 'B3'). Copy the printed tag; never count positions. Same order as `sources`.©Údefault_factoryr   Úsource_cellsc                ó^   • U Vs/ s H  o"R                  5       (       d  M  UPM     sn$ s  snf )zÑA blank piece names no printed mark, so there is nothing to ground.

The prompt asks the agent not to list blank cells, but compliance is
not exact; normalizing here makes the record deterministic either way.
)Ústrip)ÚclsÚvÚss      Ú5/home/mande/repo/quber/src/quber/agents/llm_client.pyÚdrop_blank_sourcesÚCellMerge.drop_blank_sources^   s"   € ñ Ó*š1�a§¡§	—™1Ñ*Ð*ùÒ*s   …*¡*© N©r*   r"   Úreturnr"   )Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__r   r   Ú__annotations__r    r!   r#   Úlistr&   r   Úclassmethodr-   Ú__static_attributes__r/   ó    r,   r   r   <   s    ‡ ññ Ð$dÑe€FˆCÓeÙØð+ñ€Cˆó ñ ØØkñ€Cˆó ñ Øñ€GˆYó ñ $ØðCñ€L�)ó ñ �Y Ó/Øó+ó ó 0ó+r;   r   u    ^([*â€ â€¡Â§Â¶#]{1,2})\s+(\S.*)$c                  ód   • \ rS rSr% Sr\" SSS9rS\S'   \" SS	9rS\S
'   \	" SS9SS j5       r
Srg)ÚFootnoteDefép   uo  One footnote printed below the table: its own marker plus its text.

The marker is read off the image, where the footnote line prints it â€”
formatting the image shows (a superscript, a parenthesized digit) decides
what is a marker, which downstream text parsing cannot see. An unmarked
general note (a basis-of-presentation line with no marker) has an empty
marker.
Ú zÀThe footnote's OWN marker exactly as printed at the start of its line: '1', '(1)', a letter 'a', an asterisk '*', a dagger. Empty string for an unmarked general note that opens with no marker.©Údefaultr   r   Úmarkerz0The footnote's text, without the leading marker.r   ÚtextÚafter©Úmodec                óÌ   • U R                   (       dR  [        R                  U R                  5      nU(       a,  UR	                  S5      U l         UR	                  S5      U l        U $ )uÓ  A symbol marker folded into the text lifts into the marker field.

The prompt asks for the marker and text as separate fields, but
compliance is not exact: a symbol legend line ('# Denotes a variance
of 100 percent or more') sometimes arrives with an empty marker and
the symbol leading the text. The split is unambiguous â€” a note's text
never opens with a bare marker symbol followed by a space â€” so it
normalizes here, the same way stacked marker groups split.
é   é   )rB   Ú_LEADING_SYMBOL_REÚmatchrC   Úgroup)ÚselfÚms     r,   Úlift_leading_symbolÚFootnoteDef.lift_leading_symbol„   sE   € ð �{�{Ü"×(Ñ(¨¯©Ó3ˆAÞØŸg™g a›j�”ØŸG™G A›J�”	Øˆr;   )rB   rC   N)r1   z'FootnoteDef')r2   r3   r4   r5   r6   r   rB   r7   rC   r   rO   r:   r/   r;   r,   r=   r=   p   sK   ‡ ññ Øð?ñ€FˆCó ñ Ð"TÑU€Dˆ#ÓUá˜'Ñ"óó #ór;   r=   c                  ó^   • \ rS rSr% Sr\" SS9rS\S'   \" SS9rS\S'   \" S	S
S9r	S\S'   Sr
g)ÚFootnoteMarké—   uÛ   One footnote reference marker observed on a table cell, identified by
QUOTING the carrying cell â€” the model never counts positions; it repeats
printed text, and a lookup resolves it, the same contract as `CellMerge`.
u?  ONE reference marker exactly as printed on the cell: '1', '(1)', a letter 'a', an asterisk '*', a dagger. One entry per cell that carries it. A cell printed with SEVERAL stacked markers â€” '(b)(c)(d)' â€” carries three markers: report three entries quoting the same cell, one per marker, never the concatenated string.r   r   rB   uî   The CARRYING cell's text, copied exactly from YOUR corrected markdown â€” the whole cell, not just the marker ('Segment EBITDA(3)', '90+ days past billing as a % of total(e) - corporate'). Copy the cell; never count or describe positions.Ú	cell_textÚfootnoteu�  What the marker points at, judged from the PAGE, not from the marker's wording. 'footnote': it points at a footnote printed for this table â€” a note line below the table or nearby that defines it. 'section': it is a cross-reference to a named part of the document (a numbered note in the financial statements, a schedule, an appendix) whose content lives elsewhere, not in a footnote line printed for this table.r@   zLiteral['footnote', 'section']Úkindr/   N)r2   r3   r4   r5   r6   r   rB   r7   rT   rV   r:   r/   r;   r,   rR   rR   —   sT   ‡ ññ
 ð#ñ€FˆCó ñ ð"ñ€Iˆsó ñ ,1ØðHñ
,€DÐ
(ö 
r;   rR   z^(?:\([A-Za-z0-9]{1,3}\)){2,}$z\([A-Za-z0-9]{1,3}\)c                  óT  • \ rS rSr% \" SSS9rS\S'   \" SSS9rS\S'   \" S	S
9rS\S'   \" \	SS9r
S\S'   \" \	SS9rS\S'   \" SSS9rS\S'   \" SSS9rS\S'   \" \	SS9rS\S'   \" \	SS9rS\S'   \" SSS 9\S(S! j5       5       r\" SSS 9\S)S" j5       5       r\" \	S#S9rS$\S%'   S&rg')*ÚLLMTableCorrectionéÂ   r?   z¤The sentence that introduces this table, copied exactly as printed from the page text, including any bold lead-in words it starts with. Empty if the table has none.r@   r   ÚcaptionzÅThe table's printed name, copied exactly as printed. A sentence is a caption, not a name, and the section heading is not the table's name. Empty if the table has no printed name. Never compose one.Útitleu›  Corrected markdown table. Preserve Camelot's numeric cell values exactly; only fix structure (merge multi-row headers, drop header-fragment rows, fix column alignment). Reproduce cell text verbatim â€” never add Markdown styling: no ** (bold) or * (italic) wrappers, and no character that is not in the source. A stray * reads downstream as a footnote-reference marker, so adding one corrupts footnote handling.r   Úmarkdowna\  Lines printed below the table that qualify the table (a marker on a header or cell is a reference, listed in footnote_refs, never an entry here; no printed footnote lines means an empty list): footnote lines that open with a printed marker, and short unmarked qualifications that apply to the table as a whole (a basis-of-presentation line, 'unaudited', a scale line). One entry per line, as a pair of the line's own printed marker (empty for an unmarked qualification) and its text without that marker. Narrative prose printed below the table is not a footnote and never goes here; it goes in body_text.r$   zList[FootnoteDef]Ú	footnotesa–  Narrative prose visible in the image below the table that is neither a footnote nor a table qualification: paragraphs discussing the business, sentences introducing the next table, any running text of the document. One entry per paragraph, its opening words only (the first ten or so). The document already holds this text; listing it records that it was seen and left out of the table. Empty list if none.r"   Ú	body_texta  Scale/currency unit attribution caption for the table (e.g. "(in millions, except percentages and per share data)"). This is the units line, not a footnote and not a data row. Exclude it from the markdown body and from footnotes. Empty string if the table has no such caption.Úunitsr   uÓ  How many leading rows of your corrected markdown are column-header rows, counted off the IMAGE. A single header line is 1. A stacked header â€” a spanning period or date band over a row of column labels â€” counts every stacked row ('March 31, 2026' over 'UPB | Carrying Value' is 2). 0 when the image shows data starting at the first row: a continuation fragment with no column headers of its own. Count rows of YOUR corrected markdown, after any flattening you did.r   r   Úheader_rowsu½  Every distinct FOOTNOTE REFERENCE MARKER shown on the table's headers or cells, read off the IMAGE (a superscript can be lost in the text). A marker is a superscript or parenthetical number, letter, or symbol that points to a footnote: e.g. '1', '(1)', a letter 'a', an asterisk '*', a dagger 'â€ ' or 'â€¡', or a section sign 'Â§'. Write each as it appears. A parenthesized NEGATIVE VALUE like '(84)' or a unit like '(%)'/'(Â£m)' is NOT a marker. A cross-reference to a NAMED SECTION of the document â€” '(Note 16)', 'Schedule II', '(Addendum 3)' â€” is written as printed WITH its word ('Note 16', 'Addendum 3'), never as a bare number. Empty list if the table carries no footnote reference markers.Úfootnote_refsa¶  WHERE each footnote reference marker sits: one entry per cell that carries a marker, addressed by that cell's row/col in your corrected markdown (header row is row 0), with the cell's printed Camelot address when it has one. A marker printed on several cells gets one entry per carrying cell. Every marker listed in footnote_refs should appear here unless you cannot tell which cell carries it. Empty list if the table carries no markers.úList[FootnoteMark]Úfootnote_marksrD   rE   c                ó\  • / nU Hd  nUR                  5       n[        R                  U5      (       a&  UR                  [        R                  U5      5        MS  UR                  U5        Mf     [        5       nU Vs/ s H$  ofU;   a  M
  UR                  U5      (       a  M"  UPM&     sn$ s  snf )uI  A stacked group reported as one string splits into its markers.

The prompt asks for one entry per marker, but compliance is not
exact: '(b)(c)(d)' still arrives as one string on some tables. A
strict run of short parenthesized groups splits unambiguously â€”
the same compliance normalization as `CellMerge.drop_blank_sources`.
)	r(   ÚCOMPOUND_MARKER_REÚ	fullmatchÚextendÚMARKER_GROUP_REÚfindallÚappendÚsetÚadd)r)   r*   ÚoutrB   r+   ÚseenrN   s          r,   Úsplit_compound_refsÚ&LLMTableCorrection.split_compound_refs-  sƒ   € ð ˆÛˆFØ—‘“ˆAÜ!×+Ñ+¨A×.Ñ.Ø—
‘
œ?×2Ñ2°1Ó5Ö6à—
‘
˜6Ö"ñ ô ›ˆÙÓAš3�a¨D¡y“°D·H±H¸Q·K—™3ÑAÐAùÒAs   Á;	B)ÂB)Â B)c                ó  ^• / nU Hx  mTR                   R                  5       n[        R                  U5      (       a0  UR	                  U4S j[
        R                  U5       5       5        Mg  UR                  T5        Mz     U$ )u   One mark carrying a stacked group becomes one mark per marker at
the same cell â€” same normalization as `split_compound_refs`.c              3  óF   >#   • U  H  nTR                  S U0S9v •  M     g7f)rB   )ÚupdateN)Ú
model_copy)Ú.0ÚgÚmarks     €r,   Ú	<genexpr>Ú:LLMTableCorrection.split_compound_marks.<locals>.<genexpr>J  s"   øé € ÐeÒJdÀQ˜4Ÿ?™?°8¸Q°-˜?Õ@ÒJdùs   ƒ!)rB   r(   re   rf   rg   rh   ri   rj   )r)   r*   rm   r+   rw   s       @r,   Úsplit_compound_marksÚ'LLMTableCorrection.split_compound_marksA  sh   ø€ ð
 #%ˆÛˆDØ—‘×!Ñ!Ó#ˆAÜ!×+Ñ+¨A×.Ñ.Ø—
‘
ÔeÌ/×JaÑJaÐbcÔJdÓeÖeà—
‘
˜4Ö ñ ð ˆ
r;   a&  Every output cell whose text you did not copy verbatim from a single Camelot cell serving the same role: joins ('$' + '666'), flattened header stacks, a spanning/band label replicated into several output cells (one entry PER output cell, all citing the same source), a row label extended with its footnote marker, a label moved to a new position. A single source cell is fine. Give the result and the exact source cells, verbatim from the Camelot markdown. List only source cells that contain text. Empty if every cell is a verbatim single-cell copy.zList[CellMerge]Úcell_mergesr/   Nr0   )r*   rb   r1   rb   )r2   r3   r4   r5   r   rZ   r7   r[   r\   r8   r]   r^   r_   r`   ra   rc   r   r9   ro   rz   r|   r:   r/   r;   r,   rX   rX   Â   s]  ‡ ñ Øðñ€GˆSó ñ Øð<ñ€Eˆ3ó ñ ð@ñ
€Hˆcó 
ñ $)Øð[ñ$€IÐ ó ñ !Øð9ñ
€Iˆyó 
ñ Øð=ñ	€Eˆ3ó 	ñ Øð,ñ€K�ó ñ  %ØðZñ €M�9ó ñ */ØðQñ
*€NÐ&ó 
ñ �_¨7Ñ3ØóBó ó 4ðBñ$ Ð%¨GÑ4Øó
ó ó 5ð
ñ $)Øðbñ$€K�ö r;   rX   c                  ó\   • \ rS rSr% Sr\" SS9rS\S'   \" SS9rS\S	'   \" S
S9r	S\S'   Sr
g)ÚHeadingVerdicti]  z<One nominated heading judged by the document heading review.z9The heading text, copied exactly from the nominated list.r   r   rC   u&  section_heading: a real heading that starts a section and governs the text below it â€” keep. running_header: a banner the page layout repeats at the top of its pages. column_label: a table column label that leaked into the heading stream. other_non_heading: repeated text that governs nothing.zQLiteral['section_heading', 'running_header', 'column_label', 'other_non_heading']ÚverdictzMOne line: what about the text and its repetition pattern decided the verdict.Úreasonr/   N)r2   r3   r4   r5   r6   r   rC   r7   r   r€   r:   r/   r;   r,   r~   r~   ]  sD   ‡ ÙFáÐ"]Ñ^€Dˆ#Ó^ÙafðMñb€GÐ^ó ñ Øcñ€FˆCö r;   r~   c                  ó0   • \ rS rSr% Sr\" SS9rS\S'   Srg)	ÚLLMHeadingReviewin  zEThe heading-review agent's answer: one verdict per nominated heading.z6One verdict per nominated heading, in the order given.r   zList[HeadingVerdict]Úverdictsr/   N)	r2   r3   r4   r5   r6   r   rƒ   r7   r:   r/   r;   r,   r‚   r‚   n  s   ‡ ÙOá%*ØLñ&€HÐ"ö r;   r‚   uK  You are reviewing the section headings of one parsed document.

The parser labeled certain printed lines as section headings. A real section
heading is printed once, where its section starts, and governs the text
beneath it. Page decoration is not a heading: a running header the layout
repeats at the top of many pages governs nothing, and neither does a table
column label that leaked into the heading stream.

Repetition alone does not make decoration. An author also repeats headings:
a sub-heading like "Year 2018 results:" recurs in every section it structures,
because each section discusses the same period. That is authored repetition
and it stays a `section_heading`. But a text printed as a heading more than
once on the same page is never an authored section heading â€” no author starts
the same section twice on one page; that pattern is a table column label or
other decoration.

You are given only the suspicious headings: each text below appears as a
heading on three or more pages, with its page list and where on the page it
is printed. Position is the strongest evidence. Layout decoration is stamped
at a fixed position at the very top of its pages. An authored repeat sits
lower, at varying positions, wherever its section happens to start. Decide
what each one is: a heading kept as `section_heading` stays a heading, and
any other verdict demotes it to page decoration.

When unsure, keep it. A wrongly demoted real heading silently strips section
context from every paragraph beneath it, which is far worse than keeping a
false one.

Return one verdict per heading, in the order given.
u|  You are correcting the *structure* of a table extracted from a PDF.

You are given two artifacts:

1. A rendered image of the PDF page containing the table.
2. A markdown table extracted by Camelot from the same page.

Hard constraints â€” these are non-negotiable:

- **DO NOT change any numeric values** from the Camelot markdown.
  Camelot is the source of truth for cell contents.
- **DO NOT read values from the page image**. The image is reference
  only: it shows you the layout so you can fix *structure* â€” merge
  multi-row headers, drop header-fragment rows, fix column alignment.
  Never fill cells, add rows, or invent values from what you see in the
  image. If a value is missing from the Camelot markdown, leave the
  cell empty â€” do not reconstruct it from the image.
- If the Camelot markdown is empty or has no real data, return it
  unchanged (or an empty `markdown`). Do not fabricate a table.
- Reproduce each cell's text verbatim â€” never add Markdown styling. Do not
  wrap cell text in ** (bold) or * (italic), and add no character that is not
  in the source. You may fix arrangement (column/header placement), never the
  characters. A stray * is read downstream as a footnote-reference marker, so
  adding one corrupts footnote handling.

`title`, `caption`, and `footnotes` may still be drawn from the image
â€” those are heading/caption metadata, not table cell values.
uÉ,  You vet and fix the STRUCTURE of one table extracted from a PDF, using a cropped
image of that table as the arbiter of structure and the provided text as the
source of values.

You are given four artifacts, all for ONE table:
1. A cropped image of just this table â€” the ARBITER of column and header
   structure (how many columns, where they split, multi-row headers).
2. The Camelot markdown â€” correct values, but the column structure may be wrong
   (e.g. a spurious empty column that splits a real column, or a header
   misaligned from its values). Every non-empty cell carries a printed address
   tag: `[B3] 1,637` means this cell is B3. The tags are REFERENCE ONLY â€” they
   let you name a cell by copying the tag printed next to its text. Never copy
   a tag into your corrected table; cell text is everything after the tag.
3. The table's text layer â€” the authoritative source for any value.
4. The full text of the page, in reading order â€” the source for `caption` and
   `title`. The table's own text layer can cut a line in half where two tables
   sit side by side; the page text has the whole line.

Produce:
- `markdown`: the corrected table. Make the column/header structure match the
  IMAGE â€” drop spurious empty columns, realign values under the correct headers,
  merge multi-row / fragmented headers into clear labels. If the image shows
  rows the Camelot markdown is missing, recover them from the TEXT LAYER.
- `caption`: the sentence that introduces this table, copied exactly as printed
  from the page text, including any bold lead-in words it starts with. Empty if
  the table has none.
- `title`: the table's printed name, copied exactly as printed. A sentence is a
  caption, not a name, and the section heading is not the table's name. Empty if
  no name is printed. Never compose, shorten or paraphrase either field.
- `footnotes`: the lines printed below the table that QUALIFY the table, read
  from the image; one entry per line as a PAIR: the line's OWN marker exactly as
  printed at its start ('1', '(1)', 'a', '*', a dagger), and its text without
  that marker. The image's formatting decides what is a marker. A short unmarked
  line that qualifies the whole table (a basis-of-presentation line, "unaudited",
  a scale line â€” even one starting with a number, like a year) gets an empty
  marker. Never put a footnote in the markdown body.
  Decide for every line below the table what it IS: table data (markdown), a
  footnote or table qualification (footnotes), or the document's own running
  text (body_text). A paragraph that discusses the business, states a figure
  in prose, or introduces the next table is running text even when it sits
  directly under the footnotes and even when the crop includes it in full. It
  is NOT a footnote and does not go in `footnotes`.
  A `footnotes` entry is a LINE PRINTED BELOW THE TABLE. A marker shown on a
  header or a cell is a reference to a footnote, listed in `footnote_refs`; it
  is never itself an entry, and an entry is never invented for it. If no
  footnote lines are printed in the crop, `footnotes` is an empty list, even
  when headers or cells carry markers.
- `body_text`: the running text you excluded, one entry per paragraph, its
  opening words only. Empty list if the crop shows none.
- `units`: the scale/currency attribution caption that applies to the whole table
  (e.g. "(in millions, except percentages and per share data)" or "($ in thousands)").
  Put it here and nowhere else â€” never in the markdown body and never in `footnotes`.
  Empty string if the table has no such caption.
- `header_rows`: how many leading rows of your corrected markdown are
  column-header rows, counted off the IMAGE. A single header line is 1. A
  stacked header â€” a spanning period or date band over a row of column labels â€”
  counts every stacked row ("March 31, 2026" over "UPB | Carrying Value" is 2).
  A fragment whose first row is already data is 0. Count rows of YOUR corrected
  markdown, after any flattening you did.
- `footnote_refs`: every distinct footnote reference marker shown on a header or
  cell â€” a superscript or parenthetical number, letter, or symbol that points to a
  footnote (e.g. a superscript 1, "(1)", a letter "a", "*", a dagger). A stacked
  group printed on one cell â€” "(b)(c)(d)" â€” is SEVERAL markers, listed separately
  as "(b)", "(c)", "(d)", never as one concatenated string. Read these
  off the IMAGE, since a superscript may be absent from the text. A parenthesized
  negative value like "(84)" or a unit like "(%)" is not a marker. A cross-reference
  to a NAMED SECTION of the document â€” "(Note 16)" after a row label, "Schedule II",
  "(Addendum 3)" â€” is written as printed WITH its word ("Note 16", "Addendum 3"),
  never as a bare number: a bare "16" claims a footnote printed below the table,
  which is not what the page shows. Empty if none.
- `footnote_marks`: WHERE each marker sits â€” one entry per cell that carries a
  reference marker, read off the IMAGE. Give the marker as printed and the
  carrying cell's text COPIED EXACTLY from your corrected markdown â€” the whole
  cell, never a position or a description. A marker printed on several cells
  gets one entry per carrying cell: a marker on a column header, a row label,
  and a data cell are all reported the same way. Every marker in
  `footnote_refs` should appear here unless you genuinely cannot tell which
  cell carries it. For each entry, JUDGE what the marker points at and set
  `kind`: 'footnote' when it points at a note printed for this table,
  'section' when it is a cross-reference to a named part of the document whose
  content lives elsewhere. Judge from what the page shows â€” is there a footnote
  line printed for it, or does it name a separate part of the document?
- `cell_merges`: report EVERY output cell whose text you did not copy verbatim
  from a single Camelot cell serving the same role â€” every cell you joined,
  flattened, replicated, carried, or extended. That includes, each as a normal
  case:
  * a split symbol rejoined with its number ('$' + '666');
  * stacked header rows flattened into one label ('Three Months Ended' +
    'June 30, 2024');
  * a spanning label, section band, or period band you placed into MORE THAN
    ONE output cell ('GAAP Results' printed once but serving three columns):
    report EACH output cell as its own entry, all citing the same source;
  * a row label you extended with its footnote marker from the text layer:
    cite the label's own cell;
  * a label you moved or carried to a different position.
  Header and band rows ANYWHERE in the table count â€” a period band in the
  middle of the table is reported exactly like the top header row.
  A SINGLE source cell is fine â€” `sources`/`source_cells` may hold one element.
  `result` is the COMBINED CELL'S OWN TEXT exactly as it appears in
  your corrected markdown â€” never the row label or any neighboring cell â€” and
  `row`/`col` are THAT cell's position (row 0 is the header row, columns count
  from 0 on the left). `sources` are the source cells' texts copied verbatim
  (without the address tag); `source_cells` are their printed addresses (e.g.
  ['B2', 'B3']) â€” COPY the tag printed next to that cell's text; never count
  positions, and never put an address in `sources` or text in `source_cells`.
  List only source cells that contain text â€” a blank cell you absorbed has no
  printed mark, so do not list it. If none of the cells you built a cell from
  contain text, do not report that cell at all.

Camelot emits a FLAT grid with no notion of merged or spanning cells, so the
following structural errors are EXPECTED on real tables â€” treat each as a
normal case to fix, not an exception:

- SYMBOL SPLIT INTO ITS OWN COLUMN: a currency symbol or sign ($, %, parentheses)
  that Camelot placed in a separate column belongs WITH the number beside it.
  Rejoin them into one cell (a "$" column followed by a "666" column becomes the
  single cell "$ 666"). Never leave a symbol as a standalone column.
- MULTI-LEVEL / SPANNING COLUMN HEADER: headers stacked in two or more rows
  where a top label spans several columns. Flatten them into ONE header row in
  which every column's label is its full top-to-bottom path â€” EVERY stacked
  level from the topmost group label down to the column's own label, none
  skipped. Specifically:
  * Combine each column's group label with its own period/date/year, e.g. group
    "Three Months Ended" + "June 30, 2024" -> "Three Months Ended June 30, 2024".
  * There may be SEVERAL span groups of UNEQUAL width on the same row (e.g.
    "Three Months Ended" over three columns AND "Six Months Ended" over two);
    assign each column to the group it physically sits under.
  * If a column's sub-label is partial â€” only a year, or a date left blank
    because it is shared with the column beside it â€” carry the missing piece
    from that column's group or sibling so EVERY column ends up complete
    (a year-only "2025" under the six-months group becomes "Six Months Ended
    June 30, 2025"). Use only date/period text that actually appears on the
    page; never invent a period.
  * A group label printed ABOVE the span groups is a LEVEL of every column
    under it â€” carry it too. "Coverage Data" printed over "Before Management
    Fees" and "After Management Fees" makes the flattened labels "Coverage
    Data Before Management Fees" and "Coverage Data After Management Fees".
    NEVER drop a level of the stack.
  * Emit exactly ONE header row; every column carries a complete label; no blank
    header cells and no leftover span-only row.
- UNLABELED TOTAL ROW / MISSING HEADER ROW: a row that totals its section but
  prints no label may be labeled "Total"; a table printed with no header row
  may receive generic column names ("Item", "Description"). Use ONLY these
  neutral conventional words â€” never a specific guess like "Average" or
  "Net". A label you add this way has no printed source, so never report it
  in `cell_merges`.
- INTERLEAVED EMPTY / SPACER COLUMNS: stream over-segmentation inserts blank
  columns between real ones. Drop them and bring the real columns together.
- HEADER FRAGMENTED ACROSS ROWS: merge the fragments into the single header row
  described above.

After fixing, the table must be RECTANGULAR: one header row, every body row with
the same number of columns, and no stray blank columns.

Hard constraints â€” non-negotiable:
- Every value in `markdown` must come from the Camelot markdown or the text
  layer. NEVER read a number off the image, and NEVER invent, compute, or
  estimate one.
- Preserve every Camelot value; only move it to the correct column (rejoining a
  split symbol to its number is moving, not inventing).
- Reproduce each cell's text verbatim â€” never add Markdown styling. Do not wrap
  text in ** (bold) or * (italic), and add no character that is not in the
  source. You fix a cell's ARRANGEMENT â€” which column or header it sits in â€”
  never its CHARACTERS. A stray * is read downstream as a footnote-reference
  marker, so adding one corrupts footnote handling.
- A cell that is BLANK on the page stays blank in your output. Never write a
  placeholder for an empty cell: no em-dash, hyphen, 'N/A', zero, or any other
  stand-in. Emit a dash only if that exact dash is printed on the page and
  present in the Camelot markdown or text layer.
zÇYou are auditing a page image for table coverage.

Return ONLY a single integer: the number of text-based tables visible on
the page. Do not count figures, charts, or images. Return just the number.
c                  óŒ   • \ 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r	          SS jr
SS	 jrS
rg)Ú	LLMClientim  c              ƒ  ó   #   • g 7f©Nr/   ©rM   Ú
image_paths     r,   Úcount_tablesÚLLMClient.count_tablesq  s   é € ¸3ùó   ‚c              ƒ  ó   #   • g 7fr‡   r/   ©rM   r‰   r\   s      r,   Úcorrect_structureÚLLMClient.correct_structurer  s   é € ÐhkùrŒ   c              ƒ  ó   #   • g 7fr‡   r/   ©rM   Ú	image_pngr\   Úregion_textÚ	page_texts        r,   Úvet_structureÚLLMClient.vet_structures  s   é € à'*ùrŒ   c              ƒ  ó   #   • g 7fr‡   r/   ©rM   Ú	nominateds     r,   Úreview_headingsÚLLMClient.review_headingsv  s   é € ÐSVùrŒ   c                ó   • g r‡   r/   rˆ   s     r,   Úcount_tables_syncÚLLMClient.count_tables_syncw  s   € ¸#r;   c                ó   • g r‡   r/   rŽ   s      r,   Úcorrect_structure_syncÚ LLMClient.correct_structure_syncx  s   € Ðgjr;   c                ó   • g r‡   r/   r’   s        r,   Úvet_structure_syncÚLLMClient.vet_structure_syncy  s   € à'*r;   c                ó   • g r‡   r/   r™   s     r,   Úreview_headings_syncÚLLMClient.review_headings_sync|  s   € ÐRUr;   r/   N©r‰   r   r1   r   ©r‰   r   r\   r   r1   úOptional[LLMTableCorrection]©
r“   Úbytesr\   r   r”   r   r•   r   r1   r«   ©rš   r   r1   zOptional[LLMHeadingReview])r2   r3   r4   r5   rŠ   r�   r–   r›   rž   r¡   r¤   r§   r:   r/   r;   r,   r…   r…   m  sv   † ô ?Ükð+Øð+Ø*-ð+Ø<?ð+ØLOð+à	%ô+ô WÜ=Üjð+Øð+Ø*-ð+Ø<?ð+ØLOð+à	%ô+÷ Vr;   r…   c                  ó¼   • \ rS rSrSrSSS jjrSS jrSS jr\SS j5       r	SS jr
SS jrSS	 jr          SS
 jr          SS jrSS jrSS jrSrg)ÚClaudeCLIClienti  z{Shells out to `claude -p` for subscription-backed usage. Zero API
token cost; depends on the `claude` binary being on PATH.c                ó   • Xl         X l        g r‡   ©ÚbinaryÚtimeout)rM   r³   r´   s      r,   Ú__init__ÚClaudeCLIClient.__init__ƒ  s   € ØŒØ�r;   c                ó†  • [         R                  " U R                  5      c  [        SU R                   S35      eU R                  SUSU 3-   SS/n[        R
                  " USSU R                  SS	9nUR                  S
:w  a%  [        SUR                   SUR                   35      eUR                  R                  5       $ )NÚ`z\` not found on PATH. Install Claude Code or set QUBER_LLM_BACKEND=api to use the API client.z-pz	

Image: z--output-formatrC   TF)Úcapture_outputrC   r´   Úcheckr   zclaude CLI failed (exit z): )ÚshutilÚwhichr³   ÚRuntimeErrorÚ
subprocessÚrunr´   Ú
returncodeÚstderrÚstdoutr(   )rM   Úpromptr‰   Úcmdr   s        r,   r¿   ÚClaudeCLIClient.run‡  sÍ   € Ü�<Š<˜Ÿ™Ó$Ñ,ÜØ�D—K‘K�=ð !?ð @óð ð
 �K‰KØØ�{ : ,Ð/Ñ/ØØð
ˆô —’ØØØØ—L‘LØñ
ˆð ×Ñ Ó!ÜÐ!9¸&×:KÑ:KÐ9LÈCÐPV×P]ÑP]ÈÐ_Ó`Ð`Ø�}‰}×"Ñ"Ó$Ð$r;   c                ó¾   • U R                  [        U5      nUR                  5        H%  nUR                  5       (       d  M  [	        U5      s  $    [        SU< 35      e)Nz+count_tables: could not parse integer from )r¿   ÚCOUNT_TABLES_PROMPTÚsplitÚisdigitr   Ú
ValueError)rM   r‰   ÚrawÚtokens       r,   rž   Ú!ClaudeCLIClient.count_tables_syncŸ  sM   € Ø�h‰hÔ*¨JÓ7ˆØ—Y‘Y–[ˆEØ�}‰}�‹Ü˜5“zÒ!ñ !ô ÐFÀsÁgÐNÓOÐOr;   c                ó0  •  [         R                  " U 5      $ ! [         R                   ai    U R                  S5      nU R	                  S5      nUS:  d  X!::  a   g  [         R                  " XUS-    5      s $ ! [         R                   a      g f = ff = f)NÚ{Ú}r   rH   )ÚjsonÚloadsÚJSONDecodeErrorÚfindÚrfind)rË   ÚstartÚends      r,   Úextract_json_objectÚ#ClaudeCLIClient.extract_json_object¦  s‡   € ð
	Ü—:’:˜c“?Ð"øÜ×#Ñ#ó 	Ø—H‘H˜S“MˆEØ—)‘)˜C“.ˆCØ�q‹y˜C›LÙðÜ—z’z #¨c°A©gÐ"6Ó7Ò7øÜ×'Ñ'ó Úðúð	ús.   ‚ ˜ABÁA9Á6BÁ9BÂBÂBÂBc                óž  • [         R                  " [        R                  5       SS9n[        S-   U-   S-   S-   S-   S-   S-   S	-   S
-   S-   S-   S-   S-   S-   S-   S-   S-   S-   S-   S-   S-   S-   S-   S-   S-   S-   S-   SU S3-   n[        S5       Hl  nU R                  XA5      nU R                  U5      nUc.  US:X  a  US -  nM5  [        R                  " S!UR                  S"9    g  [        R                  U5      s  $    g ! [         a@  nUS:X  a  US#U S$3-  n S nAM�  [        R                  " S%UR                  US&9   S nA  g S nAff = f)'NrI   )Úindentz@

Return a JSON object conforming exactly to this JSON Schema:

z

Field guidance:
zI- `markdown` (REQUIRED): the corrected markdown table; include every row zJfrom the Camelot extraction with cell values preserved exactly. Reproduce uL   cell text verbatim â€” no ** (bold) or * (italic) styling, and no character zHnot in the source; a stray * is read downstream as a footnote-reference zmarker.
zI- `title`: the table title from the page image (e.g. "Branded Volume and z5Transactions"); empty string if no title is visible.
zL- `subtitle`: any sub-heading below the title (e.g. "For the 3 Months Ended z,September 30, 2025"); empty string if none.
zL- `footnotes`: the lines below the table that qualify the table (marker-led zLfootnotes and short unmarked qualifications such as a basis-of-presentation zNline); one entry per line, as an object pairing its own printed marker (empty zMstring for an unmarked qualification) with its text minus that marker; empty zGlist if none. The document's running text below the table (a paragraph zIdiscussing the business, a sentence introducing the next table) is not a zDfootnote: leave it out of `footnotes` and list its opening words in z`body_text`.
zJ- `body_text`: opening words of each running-text paragraph you excluded; zempty list if none.
zN- `footnote_refs`: every distinct footnote reference marker shown on a header zPor cell, read off the image (superscript or parenthetical number/letter/symbol, zQe.g. '1', '(1)', '*', a dagger); a parenthesized negative value or a unit is not za marker; empty list if none.
uC   
Return only the JSON object â€” no prose, no markdown code fences.z

=== Camelot markdown ===
Ú
r   zy

Your previous response did not contain a valid JSON object. Return a single JSON object conforming to the schema above.zKClaudeCLIClient.correct_structure: no JSON object after retry image={image})Úimagez,

Your previous response failed validation:
z@
Return a JSON object that conforms exactly to the schema above.zXClaudeCLIClient.correct_structure: validation failed after retry image={image} exc={exc})rÝ   Úexc)rÑ   ÚdumpsrX   Úmodel_json_schemaÚCORRECT_STRUCTURE_PROMPTÚranger¿   rØ   r   ÚerrorÚnameÚmodel_validater   )	rM   r‰   r\   Úschema_blockrÃ   ÚattemptrË   ÚdatarÞ   s	            r,   r¡   Ú&ClaudeCLIClient.correct_structure_sync´  sJ  € Ü—z’zÔ"4×"FÑ"FÓ"HÐQRÑSˆô
 %ØTñUàñð &ñ&ð Zñ	Zð
 [ñ[ð ]ñ]ð YñYð ñð Zñ	Zð Gñ
Gð ]ñ]ð >ñ>ð ]ñ]ð ]ñ]ð _ñ_ð  ^ñ!^ð" Xñ#Xð$ Zñ%Zð& Uñ'Uð( ñ)ð* [ñ+[ð, &ñ-&ð. _ñ/_ð0 añ1að2 bñ3bð4 0ñ50ð6 Uñ7Uð8 /¨x¨j¸Ð;ñ9<ð 	ô> ˜Q–xˆGØ—(‘(˜6Ó.ˆCØ×+Ñ+¨CÓ0ˆDØ‰|Ø˜a“<ØðVñ�Fñ Ü—’ØaØ$Ÿ/™/òñ ðÜ)×8Ñ8¸Ó>Ò>ñ!  ð< øô #ó Ø˜a“<ØØIÈ#Èð OZð Zñ�Fõ Ü—’ØnØ$Ÿ/™/Øòõ
 ûðús   Ã(DÄ
EÄEÄ! EÅEc              ƒ  ó`   #   • [         R                  " U R                  U5      I S h  v•N $  N7fr‡   )ÚasyncioÚ	to_threadrž   rˆ   s     r,   rŠ   ÚClaudeCLIClient.count_tablesø  s$   é € Ü×&Ò& t×'=Ñ'=¸zÓJ×JÐJÑJùó   ‚%.§,¨.c              ƒ  ó`   #   • [         R                  " U R                  X5      I S h  v•N $  N7fr‡   )rë   rì   r¡   rŽ   s      r,   r�   Ú!ClaudeCLIClient.correct_structureû  s$   é € Ü×&Ò& t×'BÑ'BÀJÓY×YÐYÑYùrî   c                ó   • [        S5      e©Nz>vet_structure requires the api backend (QUBER_LLM_BACKEND=api)©ÚNotImplementedErrorr’   s        r,   r¤   Ú"ClaudeCLIClient.vet_structure_syncþ  s   € ô
 "Ð"bÓcÐcr;   c              ƒ  ó    #   • [        S5      e7frò   ró   r’   s        r,   r–   ÚClaudeCLIClient.vet_structure  s   é € ô "Ð"bÓcÐcùs   ‚c                ó   • Ung r‡   r/   ©rM   rš   Ú_s      r,   r§   Ú$ClaudeCLIClient.review_headings_sync
  s   € ð ˆØr;   c              ƒ  ó   #   • Ung 7fr‡   r/   rù   s      r,   r›   ÚClaudeCLIClient.review_headings  ó   é € ØˆØùó   ‚r²   N)Úclaudeé´   )r³   r   r´   r   r1   ÚNone)rÃ   r   r‰   r   r1   r   r©   )rË   r   r1   zOptional[Any]rª   r¬   r®   )r2   r3   r4   r5   r6   rµ   r¿   rž   ÚstaticmethodrØ   r¡   rŠ   r�   r¤   r–   r§   r›   r:   r/   r;   r,   r°   r°     s¯   † ñAöô%ô0Pð óó ðôBôHKôZðdØðdØ*-ðdØ<?ðdØLOðdà	%ôdðdØðdØ*-ðdØ<?ðdØLOðdà	%ôdô
÷r;   r°   c                  óò   • \ rS rSrSrSr   S       SS jjrSS jrSS jrSS jr	SS	 jr
SS
 jrSS jrSS jrSS jrSS jr          SS jr          SS jrSS jrSS jrSS jrSrg)ÚPydanticAIClienti  að  Uses pydantic-ai with an Anthropic model.

Auth resolution order:
1. `auth_token` arg -> Anthropic OAuth (subscription) bearer token.
2. `ANTHROPIC_AUTH_TOKEN` env var (OAuth subscription).
3. `api_key` arg     -> Anthropic API key.
4. `ANTHROPIC_API_KEY` env var (API key).

Any OAuth token wins, so an `api_key` arg is ignored while
`ANTHROPIC_AUTH_TOKEN` is set.

OAuth is preferred so the same subscription that backs `claude -p` also
drives the structured-extraction path. The OAuth path uses a wrapper
(see `agents/_oauth_gate.py`) that rewrites the system prompt into the
2-block array format the subscription gate requires. That wrapper is
a workaround for a pydantic-ai limitation tracked in
`issues/pydantic-ai-multi-block-system-prompt.md`.
g        Nc           
     óÆ  ^ ^^^^^• SSK Jn  SSKJm  SSKJm  SSKJm  [        5       R                  nU=(       d    UR                  =(       d    [        nU=(       d    UR                  mU=(       d    UR                  mT(       d  T(       d  [        S5      eSUUUU4S jjnU" U5      nUR                  =(       d    UnX�:X  a  UOU" U5      n	UT l
        UT l        SUU 4S jjn
U
" U5      nU" U[         ["        US	9T l        U" U[&        [(        US	9T l        U" U	[         [,        U
" U5      S	9T l        U" U[0        [2        U
" US
S9S	9T l        SSKJn  U" SS9T l        g )Nr   )ÚAgent)ÚAnthropicModel)ÚAnthropicProvider)ÚModelSettingszpPydanticAIClient: neither ANTHROPIC_AUTH_TOKEN nor ANTHROPIC_API_KEY is set. Provide one via env or constructor.c                óH   >• T(       a  SSK Jn  U" U T5      $ T" TS9nT" XS9$ )Nr   )Úmake_oauth_anthropic_model)Úapi_key)Úprovider)Úquber.agents._oauth_gater  )Úmodel_idr  r  r  r	  Úresolved_authÚresolved_keys      €€€€r,   Úbuild_modelÚ.PydanticAIClient.__init__.<locals>.build_modelK  s+   ø€ ÞÝOá1°(¸MÓJÐJÙ(°Ñ>ˆHÙ! (Ñ>Ð>r;   c                ón   >• [        U 5      (       a  T" SSTR                  0UD6$ U(       a  T" S0 UD6$ S $ )NÚtemperaturer/   )r   ÚDEFAULT_TEMPERATURE)r  Úextrar
  rM   s     €€r,   Úgreedy_settingsÚ2PydanticAIClient.__init__.<locals>.greedy_settings^  s<   ø€ Ü,¨X×6Ñ6Ù$ÑS°×1IÑ1IÐSÈUÑSÐSÞ-2‘=Ñ) 5Ñ)Ð<¸Ð<r;   )Úoutput_typeÚsystem_promptÚmodel_settingsi @  )Ú
max_tokens)ÚLangSmithTracerzquber-llm-client)Úrun_name)r  r   )r  r   r1   zOptional[ModelSettings])Úpydantic_air  Úpydantic_ai.models.anthropicr  Úpydantic_ai.providers.anthropicr	  Úpydantic_ai.settingsr
  r   ÚllmÚmodelr   Úanthropic_auth_tokenÚanthropic_api_keyr½   Ú	vet_modelrX   rá   Úcorrect_agentr   rÇ   Úcount_agentÚVET_STRUCTURE_PROMPTÚ	vet_agentr‚   ÚREVIEW_HEADINGS_PROMPTÚheading_agentÚquber.agents.langsmith_tracerr  Útracer)rM   r&  Ú
auth_tokenr  r  Úllm_settingsr  Ú
anth_modelr)  Úvet_anth_modelr  r  r  r  r	  r
  r  r  s   `            @@@@@r,   rµ   ÚPydanticAIClient.__init__3  sS  ý€ õ 	&Ý?ÝEÝ6ô $“~×)Ñ)ˆØ×@˜×+Ñ+×@Ô/@ˆØ"×G l×&GÑ&GˆØ×@ ,×"@Ñ"@ˆæ¦\ÜðPóð ÷
	?ò 	?ñ ! Ó'ˆ
ð
 !×*Ñ*×3¨eˆ	Ø'0Ó'9™¹{È9Ó?UˆàˆŒ
Ø"ˆŒ÷	=ð 	=ñ
 )¨Ó/ˆÙ"ØÜ*Ü2Ø)ñ	
ˆÔñ !ØÜÜ-Ø)ñ	
ˆÔñ ØÜ*Ü.Ù*¨9Ó5ñ	
ˆŒñ #ØÜ(Ü0Ù*¨5¸UÑCñ	
ˆÔõ 	Bá%Ð/AÑBˆ�r;   c                ó8   • SSK Jn  U" UR                  5       SS9$ ©Nr   )ÚBinaryContentz	image/png)rè   Ú
media_type)r!  r9  Ú
read_bytes)rM   r‰   r9  s      r,   Úbinary_contentÚPydanticAIClient.binary_contentŒ  s   € Ý-á *×"7Ñ"7Ó"9ÀkÑRÐRr;   c                ó*   • SS[         S.SSSS./S./0$ )NÚmessagesÚsystem©ÚroleÚcontentÚuserrC   úCount tables in this page.©ÚtyperC   )rÇ   )rM   s    r,   Úcount_inputsÚPydanticAIClient.count_inputs‘  s3   € àØ!Ô.AÑBà"à!'Ð1MÑNð ñðð

ð 
	
r;   c                ó*   • SS[         S.SSUS./S./0$ ©Nr?  r@  rA  rD  rC   rF  )rá   ©rM   Ú	user_texts     r,   Úcorrect_inputsÚPydanticAIClient.correct_inputsž  s0   € àØ!Ô.FÑGà"Ø)/¸Ñ CÐDñðð
ð 	
r;   c                ó|  • U R                  5       nU R                  R                  SX R                  S9 nU R                  R                  SU R                  U5      /5      n[        UR                  5      nS[        U5      S./U[        UR                  5      S.Ul        S S S 5        U$ ! , (       d  f       W$ = f©NrŠ   ©r&  rE  Ú	assistantrA  )r?  ÚcountÚusage_metadata)rH  r1  Úllm_run_syncr&  r+  Úrun_syncr<  r   Úoutputr   r   ÚusageÚoutputs©rM   r‰   Úinputsr¿   r   rT  s         r,   rž   Ú"PydanticAIClient.count_tables_sync©  s¬   € Ø×"Ñ"Ó$ˆØ�[‰[×%Ñ% n°fÇJÁJÐ%ÑOÐSVØ×%Ñ%×.Ñ.Ø-¨t×/BÑ/BÀ:Ó/NÐOóˆFô ˜Ÿ™Ó&ˆEà&1¼cÀ%»jÑIÐJØÜ"5°f·l±lÓ"CñˆCŒK÷ Pð ˆ÷ PÔOð ˆús   µA-B,Â,
B;c           
     óD  • SU S3nU R                  U5      nU R                  R                  SX@R                  S9 nU R                  R                  X0R                  U5      /5      nUR                  nSUR                  5       S./UR                  UR                  UR                  UR                   Vs/ s H  oˆR                  5       PM     sn[        UR                  5      [!        UR"                  5      S.Ul        S S S 5        U$ s  snf ! , (       d  f       W$ = f©NzCamelot markdown:
z,

Correct the structure from the page image.r�   rR  rS  rA  )r?  r[   rZ   r\   r]   r^   rU  )rN  r1  rV  r&  r*  rW  r<  rX  Úmodel_dump_jsonr[   rZ   r\   r]   Ú
model_dumpr8   r^   r   rY  rZ  ©	rM   r‰   r\   rM  r\  r¿   r   rX  Úfs	            r,   r¡   Ú'PydanticAIClient.correct_structure_sync·  sü   € Ø)¨(¨Ð3aÐbˆ	Ø×$Ñ$ YÓ/ˆØ�[‰[×%Ñ%Ð&9¸6ÏÉÐ%ÑTÐX[Ø×'Ñ'×0Ñ0°)×=PÑ=PÐQ[Ó=\Ð1]Ó^ˆFØ)/¯©ˆFà&1¸f×>TÑ>TÓ>VÑWÐXØŸ™Ø!Ÿ>™>Ø"ŸO™OØ6<×6FÒ6FÓGÒ6F°Ÿl™lžnÑ6FÑGÜ! &×"2Ñ"2Ó3Ü"5°f·l±lÓ"CñˆCŒK÷ Uð ˆùò	 H÷ UÔTð ˆús   ¼A:DÂ6DÃ2DÄDÄ
Dc              ƒ  óÒ  #   • U R                  5       nU R                  R                  SX R                  S9 IS h  v•N nU R                  R                  SU R                  U5      /5      I S h  v•N n[        UR                  5      nS[        U5      S./U[        UR                  5      S.Ul        S S S 5      IS h  v•N   U$  NŠ NX N
! , IS h  v•N  (       d  f       W$ = f7frQ  )rH  r1  Úllm_runr&  r+  r¿   r<  r   rX  r   r   rY  rZ  r[  s         r,   rŠ   ÚPydanticAIClient.count_tablesÈ  sÇ   é € Ø×"Ñ"Ó$ˆØ—;‘;×&Ñ& ~°vÇZÁZÐ&×PÑPÐTWØ×+Ñ+×/Ñ/Ø-¨t×/BÑ/BÀ:Ó/NÐOó÷ ˆFô ˜Ÿ™Ó&ˆEà&1¼cÀ%»jÑIÐJØÜ"5°f·l±lÓ"CñˆCŒK÷ Q×Pð ˆñ Qñ÷ Q×P×Pð ˆüsW   ‚9C'»C¼C'¿0CÁ/CÁ0ACÂ4C'Â?C
Ã C'ÃCÃ
C'ÃC$ÃCÃC$ÃC'c           
   ƒ  óš  #   • SU S3nU R                  U5      nU R                  R                  SX@R                  S9 IS h  v•N nU R                  R                  X0R                  U5      /5      I S h  v•N nUR                  nSUR                  5       S./UR                  UR                  UR                  UR                   Vs/ s H  oˆR                  5       PM     sn[        UR                  5      [!        UR"                  5      S.Ul        S S S 5      IS h  v•N   U$  Nâ N±s  snf  N! , IS h  v•N  (       d  f       W$ = f7fr_  )rN  r1  rf  r&  r*  r¿   r<  rX  r`  r[   rZ   r\   r]   ra  r8   r^   r   rY  rZ  rb  s	            r,   r�   Ú"PydanticAIClient.correct_structureÖ  s  é € Ø)¨(¨Ð3aÐbˆ	Ø×$Ñ$ YÓ/ˆØ—;‘;×&Ñ&Ð':¸FÏ*É*Ð&×UÑUÐY\Ø×-Ñ-×1Ñ1°9×>QÑ>QÐR\Ó>]Ð2^Ó_×_ˆFØ)/¯©ˆFà&1¸f×>TÑ>TÓ>VÑWÐXØŸ™Ø!Ÿ>™>Ø"ŸO™OØ6<×6FÒ6FÓGÒ6F°Ÿl™lžnÑ6FÑGÜ! &×"2Ñ"2Ó3Ü"5°f·l±lÓ"CñˆCŒK÷ V×Uð ˆñ VÙ_ùò H÷ V×U×Uð ˆüsm   ‚A EÁD%ÁEÁ/D0Á5D'Á6AD0ÃD)Ã!2D0ÄEÄD.ÄEÄ'D0Ä)D0Ä.EÄ0EÄ6D9Ä7EÅEc                ó   • SSK Jn  U" USS9$ r8  )r!  r9  )rM   r“   r9  s      r,   Ú
binary_pngÚPydanticAIClient.binary_pngç  s   € Ý-á )¸ÑDÐDr;   c                ó*   • SS[         S.SSUS./S./0$ rK  )r,  rL  s     r,   Ú
vet_inputsÚPydanticAIClient.vet_inputsì  s-   € àØ!Ô.BÑCØ°fÀiÑ-PÐ,QÑRðð
ð 	
r;   c                ó¶  • SU SU SU S3nU R                  U5      nU R                  R                  SX`R                  S9 nU R                  R                  XPR                  U5      /5      nUR                  n	SU	R                  5       S./U	R                  U	R                  [        UR                  5      S	.Ul        S S S 5        U	$ ! , (       d  f       W	$ = f©
Nz=== Camelot markdown ===
z

=== Table text layer ===
z

=== Page text ===
z‰

Fix the structure against the cropped image; values from Camelot or the text layer only; copy `caption` and `title` from the page text.r–   rR  rS  rA  )r?  r\   r_   rU  )rn  r1  rV  r)  r-  rW  rk  rX  r`  r\   r_   r   rY  rZ  ©
rM   r“   r\   r”   r•   rM  r\  r¿   r   rX  s
             r,   r¤   Ú#PydanticAIClient.vet_structure_syncô  sÚ   € ð )¨¨
Ð2PÐQ\ÐP]ð ^"Ø"+ ð -=ð=ð 	ð —‘ Ó+ˆØ�[‰[×%Ñ% o°vÇ^Á^Ð%ÑTÐX[Ø—^‘^×,Ñ,¨i¿¹ÈÓ9SÐ-TÓUˆFØ)/¯©ˆFà&1¸f×>TÑ>TÓ>VÑWÐXØ"ŸO™OØŸ™Ü"5°f·l±lÓ"Cñ	ˆCŒK÷ Uð ˆ÷ UÔTð ˆús   ÁA=C	Ã	
Cc              ƒ  ó  #   • SU SU SU S3nU R                  U5      nU R                  R                  SX`R                  S9 IS h  v•N nU R                  R                  XPR                  U5      /5      I S h  v•N nUR                  n	SU	R                  5       S./U	R                  U	R                  [        UR                  5      S	.Ul        S S S 5      IS h  v•N   U	$  Nš Ni N
! , IS h  v•N  (       d  f       W	$ = f7frq  )rn  r1  rf  r)  r-  r¿   rk  rX  r`  r\   r_   r   rY  rZ  rr  s
             r,   r–   ÚPydanticAIClient.vet_structure	  sñ   é € ð )¨¨
Ð2PÐQ\ÐP]ð ^"Ø"+ ð -=ð=ð 	ð —‘ Ó+ˆØ—;‘;×&Ñ& ¸ÇnÁnÐ&×UÑUÐY\ØŸ>™>×-Ñ-¨y¿/¹/È)Ó:TÐ.UÓV×VˆFØ)/¯©ˆFà&1¸f×>TÑ>TÓ>VÑWÐXØ"ŸO™OØŸ™Ü"5°f·l±lÓ"Cñ	ˆCŒK÷ V×Uð ˆñ VÙV÷ V×U×Uð ˆüs[   ‚ADÁC#Á	DÁ/C)Á;C%Á<AC)ÃDÃC'ÃDÃ%C)Ã'DÃ)DÃ/C2Ã0DÃ<Dc                ó*   • SS[         S.SSUS./S./0$ rK  )r.  rL  s     r,   Úheading_inputsÚPydanticAIClient.heading_inputs  s-   € àØ!Ô.DÑEØ°fÀiÑ-PÐ,QÑRðð
ð 	
r;   c                ó¦  • U R                  U5      nU R                  R                  SX R                  S9 nU R                  R                  U5      nUR                  nSUR                  5       S./UR                   Vs/ s H  ofR                  PM     sn[        UR                  5      S.Ul        S S S 5        U$ s  snf ! , (       d  f       W$ = f©Nr›   rR  rS  rA  )r?  rƒ   rU  )rw  r1  rV  r&  r/  rW  rX  r`  rƒ   r   r   rY  rZ  ©rM   rš   r\  r¿   r   rX  r*   s          r,   r§   Ú%PydanticAIClient.review_headings_sync&  sµ   € Ø×$Ñ$ YÓ/ˆØ�[‰[×%Ñ%Ð&7¸ÇzÁzÐ%ÑRÐVYØ×'Ñ'×0Ñ0°Ó;ˆFØ'-§}¡}ˆFà&1¸f×>TÑ>TÓ>VÑWÐXØ06·²Ó@²¨1ŸYœY±Ñ@Ü"5°f·l±lÓ"CñˆCŒK÷ Sð ˆùò A÷ SÔRð ˆús   ¶A	CÁ?B<ÂCÂ<CÃ
Cc              ƒ  óü  #   • U R                  U5      nU R                  R                  SX R                  S9 IS h  v•N nU R                  R                  U5      I S h  v•N nUR                  nSUR                  5       S./UR                   Vs/ s H  ofR                  PM     sn[        UR                  5      S.Ul        S S S 5      IS h  v•N   U$  N™ Nxs  snf  N! , IS h  v•N  (       d  f       W$ = f7frz  )rw  r1  rf  r&  r/  r¿   rX  r`  rƒ   r   r   rY  rZ  r{  s          r,   r›   Ú PydanticAIClient.review_headings2  sÌ   é € Ø×$Ñ$ YÓ/ˆØ—;‘;×&Ñ&Ð'8¸&Ï
É
Ð&×SÑSÐWZØ×-Ñ-×1Ñ1°)Ó<×<ˆFØ'-§}¡}ˆFà&1¸f×>TÑ>TÓ>VÑWÐXØ06·²Ó@²¨1ŸYœY±Ñ@Ü"5°f·l±lÓ"CñˆCŒK÷ T×Sð ˆñ TÙ<ùò A÷ T×S×Sð ˆüsi   ‚:C<¼C½C<Á C!ÁCÁ 1C!ÂCÂ&C!ÃC<ÃCÃC<ÃC!ÃC!ÃC<Ã!C9Ã'C*Ã(C9Ã4C<)r*  r+  r/  r&  r1  r-  r)  )NNN)r&  úOptional[str]r2  r  r  r  r1   r  )r‰   r   )r1   úDict[str, Any])rM  r   r1   r€  r©   rª   )r“   r­   r¬   r®   )r2   r3   r4   r5   r6   r  rµ   r<  rH  rN  rž   r¡   rŠ   r�   rk  rn  r¤   r–   rw  r§   r›   r:   r/   r;   r,   r  r    sè   † ñð6 Ðð  $Ø$(Ø!%ð	WCàðWCð "ðWCð ð	WCð
 
õWCôrSô

ô	
ôôô"ôô"Eô

ðØðØ*-ðØ<?ðØLOðà	%ôð*ØðØ*-ðØ<?ðØLOðà	%ôô*
ô
÷
r;   r  c                ó,  • SSK nSSKJn  / nU =(       d    SR                  5        HÑ  nUR	                  5       nUR                  S5      (       d  M+  UR	                  S5      R                  S5       Vs/ s H  ofR	                  5       PM     nnU(       a0  [        S U 5       5      (       a  [        S U 5       5      (       a  MŸ  UR                  U Vs/ s H  oaR                  SSU5      PM     sn5        MÓ     U(       a  U" U5      $ S$ s  snf s  snf )	zÉRemove the inline address tags from an addressed grid render,
recovering the plain table exactly as `grid_to_markdown` renders it. Used
by the mock client to echo input the way a compliant agent would.r   N)Úgrid_to_markdownr?   Ú|c              3  ó,   #   • U  H
  nS U;   v •  M     g7f)Ú-Nr/   ©ru   Úcs     r,   rx   Ú*_strip_coordinate_frame.<locals>.<genexpr>M  s   é € Ð1ª5 a˜ žª5ùó   ‚c              3  óB   #   • U  H  n[        U5      1 S k:*  v •  M     g7f)>   Ú r…  Ú:N)rk   r†  s     r,   rx   rˆ  M  s   é € Ð9cÒ]bÐXY¼#¸a»&ÂOÖ:SÒ]bùs   ‚z^\[[A-Z]+\d+\]\s*)ÚreÚ%quber.core.extractors.camelot.acquirer‚  Ú
splitlinesr(   Ú
startswithrÈ   ÚanyÚallrj   Úsub)r\   Ú_rer‚  ÚrowsÚlineÚstrippedr‡  Úcellss           r,   Ú_strip_coordinate_framer™  ?  sâ   € ó åFà€DØ—˜R×+Ñ+Ö-ˆØ—:‘:“<ˆØ×"Ñ" 3×'Ñ'ÙØ$,§N¡N°3Ó$7×$=Ñ$=¸cÔ$BÓCÒ$B˜q—‘–Ñ$BˆÐCÞ”SÑ1©5Ó1×1Ñ1´cÑ9cÑ]bÓ9c×6cÑ6cÙØ�‰Á5ÓIÂ5¸a—W‘WÐ1°2°qÖ9Á5ÑIÖJñ .ö &*Ñ˜DÓ!Ð1¨rÐ1ùò	 Dùò Js   Á3DÃD
c                  ó¬   • \ rS rSrSr  S     SS jjrSS jrSS jrSS jrSS jr	          SS	 jr
          SS
 jrSS jrSS jrSrg)ÚMockLLMClientiS  z:Returns canned responses for tests. Does not call any LLM.Nc                ó   • Xl         X l        g r‡   )Útable_countÚ
correction)rM   r�  rž  s      r,   rµ   ÚMockLLMClient.__init__V  s   € ð
 'ÔØ$�r;   c                ó   • UnU R                   $ r‡   )r�  )rM   r‰   rú   s      r,   rž   ÚMockLLMClient.count_tables_sync^  s   € ØˆØ×ÑÐr;   c                óR   • UnU R                   b  U R                   $ [        SSU/ SS9$ ©Nr?   rH   )r[   rZ   r\   r]   r`   )rž  rX   )rM   r‰   r\   rú   s       r,   r¡   Ú$MockLLMClient.correct_structure_syncb  s0   € ØˆØ�?‰?Ñ&Ø—?‘?Ð"Ü!¨°BÀÐUWÐefÑgÐgr;   c              ƒ  ó,   #   • U R                  U5      $ 7fr‡   )rž   rˆ   s     r,   rŠ   ÚMockLLMClient.count_tablesh  s   é € Ø×%Ñ% jÓ1Ð1ùr‰  c              ƒ  ó,   #   • U R                  X5      $ 7fr‡   )r¡   rŽ   s      r,   r�   ÚMockLLMClient.correct_structurek  s   é € Ø×*Ñ*¨:Ó@Ð@ùr‰  c                óh   • XU4nU R                   b  U R                   $ [        SS[        U5      / SS9$ r£  )rž  rX   r™  )rM   r“   r\   r”   r•   rú   s         r,   r¤   Ú MockLLMClient.vet_structure_syncn  sD   € ð  YÐ/ˆØ�?‰?Ñ&Ø—?‘?Ð"ô "Ø˜bÔ+BÀ8Ó+LÐXZÐhiñ
ð 	
r;   c              ƒ  ó.   #   • U R                  XX45      $ 7fr‡   )r¤   r’   s        r,   r–   ÚMockLLMClient.vet_structurez  s   é € ð ×&Ñ& y¸KÓSÐSùs   ‚c                ó   • Ung r‡   r/   rù   s      r,   r§   Ú"MockLLMClient.review_headings_sync  s
   € àˆØr;   c              ƒ  ó   #   • Ung 7fr‡   r/   rù   s      r,   r›   ÚMockLLMClient.review_headings„  rþ   rÿ   )rž  r�  )rH   N)r�  r   rž  r«   r1   r  r©   rª   r¬   r®   )r2   r3   r4   r5   r6   rµ   rž   r¡   rŠ   r�   r¤   r–   r§   r›   r:   r/   r;   r,   r›  r›  S  s®   † ÙDð Ø37ð%àð%ð 1ð%ð 
õ	%ô ôhô2ôAð

Øð

Ø*-ð

Ø<?ð

ØLOð

à	%ô

ðTØðTØ*-ðTØ<?ðTØLOðTà	%ôTô
÷
r;   r›  )ÚcliÚapiÚmockc                óÐ   • U =(       d    [        5       R                  R                  nUS:X  a
  [        5       $ US:X  a
  [	        5       $ US:X  a
  [        5       $ [        SU< S35      e)Nr±  r²  r³  zUnknown QUBER_LLM_BACKEND: z. Expected cli|api|mock.)r   r%  Úllm_backendr°   r  r›  rÊ   )ÚbackendÚselecteds     r,   Úget_llm_clientr¸  Œ  sb   € Ø×8œ,›.×,Ñ,×8Ñ8€HØ�5ÓÜÓ Ð Ø�5ÓÜÓ!Ð!Ø�6ÓÜ‹ÐÜ
Ð2°8±,Ð>VÐWÓ
XÐXr;   )r\   r   r1   r   r‡   )r¶  zOptional[Backend]r1   r…   )7r6   Ú
__future__r   rë   rÑ   r�  r»   r¾   Úpathlibr   Útypingr   r   r   r   r	   r
   r   Úlogurur   Úpydanticr   r   r   r   r   Úquber.agents.factoryr   r0  r   Úquber.settingsr   r   r   ÚcompileÚDOTALLrJ   r=   rR   re   rh   rX   r~   r‚   r.  rá   r,  rÇ   r…   r°   r  r™  r›  ÚBackendr¸  r/   r;   r,   Ú<module>rÃ     sB  ðñ'õR #ã Û Û 	Û Û Ý ß R× RÑ Rå ß XÕ Xå >Ý =ß :ô*+�	ô *+ðb —Z’ZÐ CÀRÇYÁYÓOÐ ô$�)ô $ôN!�9ô !ðN —Z’ZÐ AÓBÐ Ø—*’*Ð4Ó5€ôX˜ô Xôv�Yô ô"�yô ðÐ ðJÐ ð<jÐ ðXÐ ð ôV�ó Vó ðV÷"Sñ S÷lgñ gôT	2÷(3ñ 3ðl Ð&Ñ
'€÷Yr;   