ocr: add hpd-parsing-server.py (HPD-Parsing 1B, vendor vLLM fork, server-only) - #95
Open
davanstrien wants to merge 3 commits into
Open
ocr: add hpd-parsing-server.py (HPD-Parsing 1B, vendor vLLM fork, server-only)#95davanstrien wants to merge 3 commits into
davanstrien wants to merge 3 commits into
Conversation
…ver-only) PaddlePaddle/HPD-Parsing parses a page with hierarchical parallel decoding: a layout branch forks a child branch per region (children reuse the parent's prefix KV) with a medusa P-MTP head drafting inside each branch. 94.91 OmniDocBench v1.6 at 4,752 TPS. Server-only by design — forking is a scheduler feature of Paddle's vLLM fork (0.17.1+hpdparsing), so an offline generate loop forfeits the point of the model. The fork ships only in the vendor image, whose own entrypoint is the serve command that build_serve_args() reproduces. Recipe writes `markdown` plus `hpd_blocks` (type, bbox, text per region, in reading order); bboxes are 0-1000 normalized, recorded in inference_info so downstream code can't guess wrong. `--keep-raw` retains the tag stream. Four sharp edges documented in ocr/CLAUDE.md: - vendor image has no uv, so `hf jobs uv run --image` fails; bootstrap uv in the job command (first non-Docker-Hub image in the collection, ~11 GB pull) - the entrypoint's speculative path `PaddlePaddle/HPD-Parsing/P-MTP` is neither a repo id nor a directory; resolve_spec_model() materializes it locally - `--enable-prefix-caching` stays on, against the usual OCR serve pattern, because forked children share the parent prefix KV - bboxes are normalized, not pixels, unlike surya_blocks Smoke-tested on l4x1 / davanstrien/ufo-ColPali: 5/5 pages, 0 errors, 1.00 img/s, spec-decode mean acceptance length 4.01 @ 50.1% draft acceptance, 52.9% prefix-cache hit rate; bbox space confirmed by overlay render. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…tions) Third smoke run on 12 shuffled opendatalab/OmniDocBench pages - the model's own eval set - closes the gap the ufo-ColPali runs left: 6 table blocks (HTML with rowspan/colspan) and 4 equation blocks (LaTeX) now exercised, 12/12, 0 errors. chart/seal remain unseen. Also records two benign log lines so they don't get re-debugged: the P-MTP 'Error retrieving safetensors' (head loads regardless - spec decoding reports up to 96% draft acceptance) and the tokenizer sequence-length warning on a 61.7 MPx page (no request errored; --max-pixels / --max-model-len are the knobs if it ever does). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
12 Chronicling America pages (1774-1809, LoC via IIIF): 10 excellent - output length within 1% of LoC's own OCR and visibly cleaner - but 2 collapsed into degenerate repetition, both dense 1809 ad pages that fragmented into 279-340 blocks. One emitted 726,706 chars against 24,907 in the reference OCR. Root cause is structural: hierarchical decoding gives each forked child branch its own --max-tokens budget, so a page fragmenting into hundreds of regions can emit ~100x the per-branch limit. Nothing errors, nothing truncates, throughput craters (0.05 img/s vs 0.48 on OmniDocBench). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds
ocr/hpd-parsing-server.pyfor PaddlePaddle/HPD-Parsing (1B, Apache-2.0) — a document parser that forks a child decoding branch per page region, with a medusa P-MTP head drafting inside each branch. 94.91 OmniDocBench v1.6 at 4,752 TPS.It earns a slot on layout output + throughput, not accuracy — 94.91 sits below
ovis-ocr2(96.58) andpaddleocr-vl-1.6(96.33). What it gives that those don't is its native<BLOCK> <type> [bbox] <CHILD>stream: block types, bboxes and reading order in one pass, kept in anhpd_blockscolumn alongside the markdown.Two things reviewers should look at first
1. First non-Docker-Hub image in the collection. The model only runs on Paddle's vLLM fork (
0.17.1+hpdparsing), which ships in their own registry image. It pulls anonymously (verified) and HF Jobs can fetch it, but it's ~11 GB and every job re-pulls it.build_serve_args()is the image's own/home/hpd/entrypoint.sh, not our translation.2. Server-only by design, no offline sibling. Forking is a scheduler feature, so an offline
llm.generateloop forfeits the point of the model — the vendor agrees, their image entrypoint is the serve command.Sharp edges (all in
ocr/CLAUDE.md)uv, sohf jobs uv run --imagefails; the job command bootstraps itPaddlePaddle/HPD-Parsing/P-MTPis neither a repo id nor a directory — vLLM rejects it outside the image's offline mode;resolve_spec_model()materializes it locally--enable-prefix-cachingstays on, against SERVING.md's usual OCR pattern, because forked children share the parent prefix KV (52.9% hit rate measured)surya_blocks), stamped ininference_info.bbox_spaceTesting
ufo-ColPali5 + shuffled 10OmniDocBench12 shuffledKnown failure mode, documented not fixed
On dense 1809 advertisement pages that fragment into 279–340 blocks, output degenerates into repetition loops; one page emitted 726,706 chars against 24,907 in the reference OCR. Structural cause:
--max-tokensbounds one branch, not the page, so hundreds of forked children can each spend the full budget. Nothing errors and nothing truncates. A per-block repetition guard + repetition flag is the follow-up (pairs with the deferredocr_errorcolumn); flagging rather than silently trimming, since upstream's own postprocessing does no repetition cleanup.Also unexercised:
chartandsealblocks, the two types whose text is deliberately suppressed.🤖 Generated with Claude Code