Instructions to use nvidia/Nemotron-3.5-Parse with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use nvidia/Nemotron-3.5-Parse with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("image-text-to-text", model="nvidia/Nemotron-3.5-Parse", trust_remote_code=True) messages = [ { "role": "user", "content": [ {"type": "image", "url": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/p-blog/candy.JPG"}, {"type": "text", "text": "What animal is on the candy?"} ] }, ] pipe(text=messages)# pip install -U transformers accelerate # Load model directly from transformers import AutoModelForCausalLM model = AutoModelForCausalLM.from_pretrained("nvidia/Nemotron-3.5-Parse", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use nvidia/Nemotron-3.5-Parse with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "nvidia/Nemotron-3.5-Parse" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "nvidia/Nemotron-3.5-Parse", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }'Use Docker
docker model run hf.co/nvidia/Nemotron-3.5-Parse
- SGLang
How to use nvidia/Nemotron-3.5-Parse with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "nvidia/Nemotron-3.5-Parse" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "nvidia/Nemotron-3.5-Parse", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "nvidia/Nemotron-3.5-Parse" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "nvidia/Nemotron-3.5-Parse", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }' - Docker Model Runner
How to use nvidia/Nemotron-3.5-Parse with Docker Model Runner:
docker model run hf.co/nvidia/Nemotron-3.5-Parse
Download docs/postprocessing.md from nvidia/Nemotron-3.5-Parse: direct link, hf CLI and curl.
- Browser
- Download file 23.8 kB
-
https://huggingface.co/nvidia/Nemotron-3.5-Parse/resolve/main/docs/postprocessing.md
- Command line
-
hf download hf://nvidia/Nemotron-3.5-Parse/docs/postprocessing.md
-
curl -L -o postprocessing.md https://huggingface.co/nvidia/Nemotron-3.5-Parse/resolve/main/docs/postprocessing.md
Document postprocessing and multipass parsing
The public helpers operate on model predictions and ordinary page images. They do not load benchmark labels, scorer code, category IDs, reference answers, or page-specific rules. They are adapted from the general conversion and crop-merge components of the bundled ParseBench pipeline. The pinned reproduction bundle remains unchanged so its recorded hashes and results stay reproducible.
Installation
For vLLM, use the model card's repository-source installation:
hf download nvidia/Nemotron-3.5-Parse --local-dir nemotron-parse
cd nemotron-parse
python -m pip install '.[vllm,client]'
This installs the plugin, public nemotron_parse Python package, compatibility
modules, and nemotron-parse command. No prebuilt wheel is required.
The repository source installs the vLLM plugin, parsing helpers and CLI together.
Archived release artifacts and their digests are listed in the
build evidence.
Use version 3.5.0.post5 or later for the restored merge default and post7 or later
for the corrected third-party notices.
For Transformers, keep the model card's inference dependencies. To download the helpers without installing the package:
python -m pip install 'beautifulsoup4==4.13.3' 'Pillow==12.2.0' 'numpy==2.2.6' 'httpx==0.28.1'
hf download nvidia/Nemotron-3.5-Parse \
--include 'postprocessing.py' 'latex2html.py' 'nemotron_parse/**' --local-dir .
Run Python in that directory, or put it on your Python import path. Basic text/table postprocessing only needs Beautiful Soup; the image wrappers also need Pillow and NumPy. The HTTP backend needs httpx. Downloaded source and installed packages provide the same public API.
Render an existing prediction
from nemotron_parse import postprocess_document, PostprocessOptions
options = PostprocessOptions(
infer_headers=True,
render_charts=True,
split_chart_value_percent=True,
relocate_chart_captions=True,
)
document = postprocess_document(raw_model_output, image_size=(width, height), options=options)
print(document['markdown'])
print(document['blocks'])
raw_out contains the cleaned model serialization. Each block has its original
class, normalized bbox, literal text, and rendered content. Supplying
image_size adds clipped, ordered bbox_pixels. markdown renders the complete
page in model order, with section headings and HTML tables for span preservation.
Raw per-block text remains available independently of presentation changes.
Malformed table markup that cannot be converted is preserved rather than passed
through a lossy row splitter; document warnings identify these blocks.
Default cleanup preserves empty cells, spans, line breaks, nested inline styles,
and escaped literals. It removes exact repeated blocks at identical coordinates,
repairs empty incomplete serialization prefixes, removes internal continuation
markers, and normalizes existing bullets and soft hyphens in textual roles.
Code/math roles and marked code/math text are protected from soft-hyphen cleanup.
Table conversion is applied to Table/Chart blocks; literal LaTeX in prose, code,
formulas, or Picture blocks retains its source representation.
Header inference uses predicted span topology and existing bold styling, never an
English vocabulary list. It can still make mistakes; disable infer_headers to
use only explicit predicted headers. Unknown classes and page numbers are retained.
Chart titles, subtitles, and explicit panel labels can be rendered with their associated tables. Two guarded presentation choices are enabled by default and can be disabled:
split_chart_value_percent=Truesplits consistently predicted numeric pairs such as32 (16.6%)into Value and Percent subcolumns. It requires at least two matching nonempty cells, adds those column labels, and does not calculate values.relocate_chart_captions=Trueassociates a plain caption immediately above a chart when geometry and text-preservation checks pass. Notes below remain standalone. Setrelocate_chart_captions=Falseto retain the standalone caption in its source order.
normalize_text=False, deduplicate=False, and render_charts=False disable the
corresponding cleanup/rendering choices. The existing root imports continue to
work:
from postprocessing import extract_classes_bboxes, postprocess_text
from latex2html import convert_html_table_to_json_hierarchical
postprocess_text(..., cls='Table', table_format=...) supports LaTeX, HTML,
Markdown, record JSON, hierarchical JSON, and CSV. HTML retains merged-cell
structure. Flat exports expand spans and use neutral column names for headerless
tables without consuming the first data row as a header. Duplicate column names
are disambiguated without overwriting values. Hierarchical JSON retains the
existing headers, columns, and flat data fields and additionally exposes
body_cells with explicit text, span, and header-role metadata.
Parse a page with a running vLLM server
Start the server using the model card. Then:
from nemotron_parse.inference import DocumentParser
with DocumentParser(base_url='http://localhost:8000/v1', model='nemotron-parse') as parser:
single = parser.parse('page.png')
double = parser.parse_double('page.png')
triple = parser.parse_triple('page.png', triple_regions='union')
documents = parser.parse_many(['one.png', 'two.png'], mode='double', concurrency=128)
| Mode | Work performed |
|---|---|
| Single | Full-page parse and document rendering |
| Double | Full-page parse, then Table/Chart crops from its predicted regions |
| Triple | Full-page parse, separate detection, then Table/Chart crop parsing |
A pass denotes a stage, not one request: each retained crop needs its own model
call. triple_regions='union' retains detector regions first and adds parser
regions after overlap filtering; 'detector' uses only the separate detector.
The region budget defaults to 80 per page and can be changed with max_regions.
Crops use 4% plus 12-pixel margins and JPEG quality 95, configurable margins, and
content-normalized coordinates mapped back to the original image. Tiny crops are
skipped. Crops are made in memory and no temporary image files remain.
The default merge_policy="alternatives" preserves differing overlapping
Table/Chart predictions from the full-page and crop passes. Near-identical blocks
are deduplicated using geometry and text. Guarded table refinement checks native
grid validity and compatible column counts; the original block order is preserved
and crop alternatives are inserted by their predicted geometry. This policy
matches the reference approach using only model predictions, without labels,
benchmark categories or scorer feedback.
A returned document can contain overlapping or conflicting Table/Chart hypotheses.
Use DocumentParser(merge_policy="guarded") (CLI: --merge-policy guarded) when
one selected prediction per overlapping same-class region is preferable. This
option applies conservative structural replacement and rejects overlapping
same-class additions. It can discard a correct alternative, reducing recall.
Raw stage responses remain available with either policy.
Returned results include stages with original raw responses and usage, and
inference with request counts, regions, sampling, and crop/rendering settings.
Empty, failed, filtered, or token-limited generations raise GenerationError;
its stage and response expose the failed response. Successful stage responses
from the same page are only returned when parsing completes. No automatic retry
policy changes the request settings.
Sampling is configurable per stage:
from nemotron_parse.inference import DocumentParser, GenerationSettings
with DocumentParser(
fullpage=GenerationSettings(temperature=0, top_k=1, repetition_penalty=1.1),
detection=GenerationSettings(temperature=0, top_k=1, max_tokens=4096),
crop=GenerationSettings(temperature=0, top_k=1, repetition_penalty=1.0),
) as parser:
document = parser.parse_double('page.png')
Defaults are temperature 0.2 / repetition penalty 1.05 for full-page and detection, and 0.1 / 1.0 for crops, top-k 50, top-p 1.0, seed 0. Token caps are 8,744 for parsing and 4,096 for detection. These are practical starting settings rather than a claim of optimal sampling for every document type.
Use an already loaded Transformers model
Continue from the model card's Transformers setup:
from nemotron_parse.inference import DocumentParser, TransformersBackend
backend = TransformersBackend(model, processor, generation_config=generation_config)
with DocumentParser(backend=backend) as parser:
document = parser.parse_triple(image)
This uses the same image/crop orchestration without a server or plugin. The adapter serializes calls to the shared model, uses inference mode, applies stage settings, and restores the CPU/current-GPU RNG after generation. Model weights and processor remain caller-owned. Reaching the token cap is conservatively reported as truncation, including when generation forces an EOS at the cap.
Other inference implementations can supply a callable
backend(image, *, prompt, stage, settings) returning
{'raw_out': text, 'finish_reason': 'stop', 'usage': {...}}. A string return is also
accepted, but then the custom backend must detect truncation itself. Custom
backends used with parse_many should support concurrent calls or use concurrency 1.
Command line
nemotron-parse --image page.png --mode double --output parsed-page
nemotron-parse --image-dir pages --mode triple --triple-regions union --output parsed-pages
The download-only equivalent is python -m nemotron_parse.inference with the same
arguments. The default outputs are compact documents.jsonl.gz and metrics.json, containing
blocks, raw stages, settings and timing. Add --write-page-files for individual
Markdown and JSON exports. Use a new/empty output directory. Files are written only
after all pages complete. The API accepts PIL images and image paths; the CLI
accepts image files. Render PDF pages to images before calling it.
Validation and scope
Run the focused CPU regression suite from the downloaded repository:
python -m pip install pytest
python -m pytest -q tests/test_postprocessing.py tests/test_inference.py tests/test_batch.py
The 86 focused tests passed from both source and the installed wheel. They cover table structure, text preservation, chart defaults and opt-outs, crop geometry, merge guards, HTTP handling, explicit truncation reporting, batch ordering, independent-stage overlap, early crop submission, a shared concurrency bound, and staged/pipelined region and output equivalence with deterministic responses. The Transformers adapter is checked with a small CPU model stub. The real-model H100 runs and official full-page scoring are recorded below and in the validation receipt. The pinned reproduction bundle and its recorded headline result remain unchanged.
Prepared batches and timing
parser.prepare(images, workers=8) returns a reusable in-memory batch containing
decoded RGB pixels and encoded image requests. Existing RGB JPEG/PNG/WebP payloads
are retained without another lossy encode. Other inputs are encoded as PNG.
The preparation time is reported separately, and cached images can occupy several
GB for thousands of document pages.
parser.parse_prepared(batch, mode="double", concurrency=128, postprocess_workers=8)
returns a BatchResult with documents and metrics. The default
schedule="pipelined" overlaps independent full-page and detection requests,
submits detector crops as soon as their regions are ready, and adds parser-only
regions in the original union order. Double-pass crops similarly start as each
page finishes its full-page request. Ready crops take the next available HTTP
slot, and new pages fill remaining slots. All stages share one bounded
concurrency window, including requests waiting inside vLLM. Crop preparation
uses separate CPU threads; finished pages merge and render while other pages
continue inference. Large batches use spawned rendering processes.
Run standalone scripts under a main guard, or choose postprocess_workers=1.
HTTP parse_many uses this same pipelined batch engine and returns only the
documents. Its default request window is 128; a custom backend retains the
four-worker default. Pass concurrency=1 for a backend that requires serial calls.
Use schedule="staged" for stage-barrier comparisons. A custom Transformers
backend still serializes its shared model calls.
Metrics include separately reported initial preparation, pipeline elapsed time,
request counts, token-limit counts, and processed-page and fully completed-page
throughput. Pipeline time excludes
initial preparation and output-file writing; model-dependent crop preparation is
included. A benchmark should also report serialization and warmup/server-startup
exclusions explicitly. The same prepared batch can be reused for different modes;
each call generates its own responses and provides an independent pipeline timing.
Pipelined stage spans overlap and must not be added together.
request_service_seconds sums concurrent request latency, including server queue
wait; it is not GPU compute time. peak_inflight_requests records the client
window, while actual server running/waiting requests require vLLM metrics.
Strict generation handling remains the default. Set allow_partial=True (CLI:
--allow-partial) only when partial results are useful: capped raw responses are
preserved, affected pages have complete=False and generation_warnings, and
truncated crops are excluded from refinement. HTTP/transport errors and empty
responses still raise. A successful benchmark execution does not imply that every
page completed within its token limit; inspect both coverage and throughput.
The CLI supports --no-split-chart-value-percent and
--no-relocate-chart-captions to disable the default presentation rules.
Measured prepared-batch performance
The following matched scheduler comparison used the earlier guarded merge policy in package 3.5.0.post4. The restored default and its fresh results are in accuracy restoration.
Measured on all 2,078 unique ParseBench pages with one H100 80GB, vLLM 0.26.0,
--distributed-executor-backend uni, tensor parallelism 1, one-token MTP, FA3,
max_num_seqs=192, max_num_batched_tokens=16384, and max_model_len=16384.
Both chart presentation rules were enabled. Each run generated all of its own
responses. The same-server comparison used the same prepared images and
per-mode concurrency limit for both schedulers.
| Mode | Concurrency | Staged pages/s | Pipelined pages/s | Pipelined elapsed | Fully completed pages |
|---|---|---|---|---|---|
| Single | 128 | 14.56 | 14.91 | 139.4 s | 2047/2078 |
| Double | 256 | 11.16 | 11.87 | 175.0 s | 2038/2078 |
| Triple | 128 | 7.31 | 10.90 | 190.6 s | 2039/2078 |
Initial decoding/encoding of the full batch took 9.7 seconds and was excluded from these rates. Timed work includes all model calls, predicted-region crop preparation, merge and rendering; startup, warmup and output serialization are excluded. The receipt separately reports serialization-inclusive rates.
These diagnostic runs explicitly enabled allow_partial=True. Token-limited
responses remain visible and affected pages count as incomplete. The table reports
processed-page throughput; fully completed-page throughput and cap counts are in
the benchmark receipt. Strict failure handling remains
the API default. A successful scoring run does not make truncated generations complete.
Concurrency 128 and 256 were compared on a 256-page pilot; the selected settings above were then checked on the full batch. The earlier full single-pass sweep reached 15.09 pages/s at 256. These are measured serving profiles, not a throughput ceiling. Sampling is nonzero: changed batching can change output lengths and crop counts. The receipt includes request and completion-token totals for each run. Stages share one bounded client request window. In the pipelined runner, crops receive available slots while independent full-page/detection requests continue. Stage spans overlap; adding them does not produce elapsed time. Queue averages include pipeline ramp-up and final drain, so mean running-request counts need not equal the configured limit.
Quality and the reference output contract
| Previous guarded merge, pipelined | Official overall (%) |
|---|---|
| Single | 72.79 |
| Double | 73.28 |
| Triple | 73.48 |
Each full-run output shown here was freshly scored on all 2,078 pages with the
pinned official scorer
9af4102e26b51f234ab5f037e19fec05c0696ec6, with all 241 Python source hashes
verified and zero official scoring errors. Capped generations are included in scoring.
The pinned reference merge can retain an original Table/Chart block and an
overlapping crop alternative when their text differs. The chart evaluator searches
rendered tables for the best matching data. The previous guarded policy uses
replacement and rejects overlapping same-class crop additions; raw alternatives
remain in stages.crops for inspection.
A controlled replay of identical baseline triple responses scored 73.44 with the previous guarded merge and 75.19 with reference-style merging of the same completed crops. The latter inserted 1,660 overlapping alternative blocks. This identified the output contract as the cause of the score gap. Package 3.5.0.post5 restores alternatives as the default, keeping the faster scheduler. Use the separately pinned reproduction pipeline for the exact headline 75.20 contract. The receipt includes category metrics and the merge-only replay checks.
Accuracy restoration with the faster scheduler
Package 3.5.0.post5 defaults to merge_policy="alternatives". The scheduler,
model settings, region selection and chart presentation defaults are unchanged.
The merge retains differing full-page/crop hypotheses as the reference does;
it adds no model calls. merge_policy="guarded" remains available explicitly.
Fresh model inference on all 2,078 pages with one H100, the same uni/FA3/MTP server profile, and prepared inputs produced:
| Pipeline | Concurrency | Pages/s | Elapsed | Official overall (%) | Fully completed pages |
|---|---|---|---|---|---|
| Double | 256 | 11.64 | 178.5 s | 74.92 | 2043/2078 |
| Triple | 128 | 10.61 | 195.8 s | 75.20 | 2038/2078 |
Timing includes crop preparation and merge/rendering. Preparation, startup, warmup
and serialization are excluded; the receipt also reports serialization-inclusive
and fully completed-page rates. These diagnostic runs explicitly used
allow_partial=True; token-limit cases remain marked incomplete.
The same saved responses from the earlier staged and pipelined runs were also rescored with the restored merge. This isolates the merge from generation:
| Saved responses | Guarded overall (%) | Alternatives overall (%) |
|---|---|---|
| Double Staged | 73.25 | 74.88 |
| Double Pipelined | 73.28 | 74.96 |
| Triple Staged | 73.51 | 75.23 |
| Triple Pipelined | 73.48 | 75.23 |
All six outputs were freshly scored on 2,078 unique pages with the pinned official scorer and all 241 source hashes verified, with zero official scoring errors. Batching at nonzero temperature can change individual responses; this does not establish bit-identical generations or a guarantee for every document.
The reference-compatible score includes alternative tables/charts. The evaluator can match the best alternative, so score recovery does not prove that a single chosen transcription is equally accurate. The alternatives are present in the actual returned document and its raw stages. Use the guarded policy for a more compact document when that tradeoff fits the application.
See the restoration receipt for category scores, generation coverage, exact settings, request/token counts, hashes and controlled replay results.
Inference throughput with prepared pages
For prediction throughput, use result.metrics["inference_pages_per_second"]
(package 3.5.0.post6 or later). Its wall clock runs from the first model request
start to the last model response across all passes. It includes server queueing,
image encoding inside the server, generation, HTTP transfer and any gaps waiting
for predicted-region selection, crop encoding or dispatch. It excludes initial
page preparation, startup/warmup, final merge/rendering after inference, and writes.
pipeline_seconds and processed_pages_per_second retain their earlier meanings,
including merge/rendering, for applications that need complete documents.
Concurrent rendering can still compete for CPU during inference; that observed
contention remains in the wall time. We do not subtract overlapping CPU time or
sum concurrent request latencies to claim a higher rate.
The fastest observed profiles compatible with the restored output quality are:
| Pipeline | Concurrency | Schedule | Prediction pages/s | Inference wall time | Fully completed pages |
|---|---|---|---|---|---|
| Single | 256 | Staged | 15.42 | 134.78 s | 2045/2078 |
| Double | 256 | Pipelined | 11.65 | 178.35 s | 2043/2078 |
| Triple | 128 | Pipelined | 10.62 | 195.70 s | 2038/2078 |
These use 2,078 unique prepared ParseBench page images on one H100 80GB and the same uni/FA3/MTP serving profile. The single-pass measurement predates the merge restoration; it has no crops and is unaffected by that merge change. Its saved stage clock includes executor setup/teardown and is slightly conservative. Double/triple times are reconstructed exactly from saved stage start/end spans, including gaps between calls. These are existing measured runs, not a new GPU run of the timing-only post6 release. See the timing receipt.
The earlier guarded-merge runs reached 11.88/10.91 prediction pages/s for double/triple, respectively, but had the lower scores documented above. Use the restored profiles when comparing speed while preserving the reference output contract. The restored double/triple scores are 74.92/75.20.
This is realistic batch prediction throughput when pages are prepared ahead of
time and enough work is queued. It is not PDF ingestion throughput or single-page
latency, and is not a measured hardware ceiling. Document complexity, generation
length, crop count, CPU capacity, network placement and batching affect speed.
The numerator includes token-limited pages; these diagnostic runs explicitly
allow partial output. Use complete_inference_pages_per_second for the number
of fully completed pages divided by the same wall time. Strict errors remain the
default outside these diagnostic runs.
prepared = parser.prepare(page_images, workers=8) # outside inference clock
result = parser.parse_prepared(
prepared, mode="triple", concurrency=128, postprocess_workers=8,
)
print(result.metrics["inference_seconds"])
print(result.metrics["inference_pages_per_second"])
For double pass, use mode="double", concurrency=256. The measured fastest
single-pass profile uses mode="single", concurrency=256, schedule="staged";
a single-pass pipelined run at concurrency 128 measured 14.92 prediction pages/s.
A pipelined single-pass run at concurrency 256 has not been measured here.