# Parse documents and export tables These helpers use predictions and page images; they do not load benchmark answers. ## Downloaded helpers With [the native vLLM server](serving.md#native-dflash2) running: ```bash python -m pip install beautifulsoup4 Pillow numpy httpx ``` No serving plugin is required. Add the downloaded repository to Python's import path: ```python import sys sys.path.insert(0, "./parse35") from nemotron_parse.inference import DocumentParser def main(): with DocumentParser( base_url="http://localhost:8000/v1", model="nemotron-parse", ) as parser: documents = parser.parse_many( ["one.png", "two.png"], mode="triple", concurrency=128, triple_regions="union", ) for document in documents: print(document["markdown"]) print(document["blocks"]) if __name__ == "__main__": main() ``` `single` parses each full page. `double` also refines predicted Table/Chart crops. `triple` adds independent detection and refines the detector/parser region union. `parse_many` overlaps parsing and detection, submits crops as they become ready, and shares one concurrency window across stages. Put multiprocessing entry points under the main guard. The one-page `parse`, `parse_double` and `parse_triple` convenience methods execute their dependent stages serially. The default alternatives merge retains differing overlapping table/chart hypotheses. Use `DocumentParser(merge_policy="guarded")` for conservative replacement and a single selected hypothesis; this can discard correct alternatives. Stage responses remain available in `document["stages"]`. ## Convert an existing prediction ```python from nemotron_parse import postprocess_document, PostprocessOptions from postprocessing import postprocess_text options = PostprocessOptions( infer_headers=True, render_charts=True, split_chart_value_percent=True, relocate_chart_captions=True, ) document = postprocess_document(raw_output, image_size=image.size, options=options) table = next(b for b in document["blocks"] if b["class"] == "Table") print(postprocess_text(table["text"], cls="Table", table_format="csv")) print(postprocess_text(table["text"], cls="Table", table_format="json_hierarchical")) ``` Blocks retain class, normalized `bbox`, literal `text` and rendered `content`. `image_size=(width,height)` adds clipped pixel boxes. Document Markdown uses HTML tables to preserve merged cells. `HTML` and `json_hierarchical` preserve cell spans; `markdown`, `csv` and record `json` are flat exports that expand spans. `latex` retains the predicted table representation. Headerless tables retain their first data row; duplicate column names are disambiguated. Set any presentation option above to `False` to disable it. Caption association moves existing text; value/percentage splitting separates existing predictions and does not calculate values. Raw block text remains available. Malformed tables are retained and reported in warnings rather than forced into a lossy row splitter. For strict output schemas, select fields after a full-structure parse; prompt switches can leave residual structural tokens. ## Draw predicted boxes Continue from the prediction conversion above to save an image overlay: ```python from PIL import ImageDraw overlay = image.copy() draw = ImageDraw.Draw(overlay) for block in document["blocks"]: if block.get("bbox_pixels"): draw.rectangle(block["bbox_pixels"], outline="red", width=2) overlay.save("prediction.png") ``` Use the same image dimensions supplied to `postprocess_document`. This recipe draws the predicted boxes without changing their geometry or the source image. ## Completion and prepared batches Strict generation handling is the default. Token-limited, empty, failed or filtered output raises `GenerationError`. If partial results are useful, pass `allow_partial=True`: affected documents have `complete=False` and `generation_warnings`; truncated crops are excluded from refinement. Transport errors and empty responses still raise. Do not count partial pages as completed. For repeated processing of the same page images: ```python prepared = parser.prepare(page_images, workers=8) result = parser.parse_prepared( prepared, mode="triple", concurrency=128, postprocess_workers=8, triple_regions="union", schedule="pipelined", allow_partial=True, ) print(result.metrics["inference_pages_per_second"]) print(result.metrics["complete_inference_pages_per_second"]) ``` Run this inside the guarded main function while `parser` is open. Preparation is reported separately. The inference clock includes model/HTTP calls, queueing and dependent crop preparation; final rendering and serialization are reported separately. Use pipeline rates when end-to-end merge/render cost matters. Stage spans overlap and must not be added together. For crop margins, generation controls, a loaded Transformers backend, CLI use, and historical validation, see the [extended reference](postprocessing-reference.md). The CLI can run from downloaded source as `python -m nemotron_parse.inference`; installing the serving plugin is unnecessary for that invocation.