ocr: the accents were never lost by the model, so take them back from its bytes (v0.3.28)
MEASURED VERDICT. The byte trace on a page of crisp synthetic Italian, served
through the Aruba install on vLLM 0.27.1:
unreadable: count=29 blame='the serving path'
page 0: calls=1 with_logprobs=1 conclusive=True kinds=None
why: the byte stream decoded cleanly, so the characters were lost AFTER
the model: the fault is in the serving path, not the model
surya-ocr-2's own byte stream was valid UTF-8 and every line came back at
conf=0.9605. The model read the page correctly; something after it destroyed 29
accented characters.
What that does NOT implicate: surya-ocr-2 and chandra-ocr-2 are the SAME
architecture (Qwen3_5ForConditionalGeneration) on the same vLLM, and chandra's
accents survive. So this is not vLLM mishandling qwen3_5 -- it is vLLM
mishandling SURYA'S CUSTOM 65,425-entry BYTE-LEVEL vocabulary, in which one
accented letter is two tokens and either byte alone decodes to a marker. It was
also a single model call, which kills the concurrent-batching hypothesis I had
been carrying.
OPTION 1, shipped and on by default: ocr.surya_repair_accents rebuilds the
reply from the per-token bytes in the same response. Applied in the backend
wrapper BEFORE surya parses the HTML, so blocks, boxes, text and confidences are
all built from correct characters; repairing further down would mean fixing the
same text in several places and missing one.
Rewriting a model's output is serious, so it refuses unless all three hold:
strict UTF-8 decode, no marker in the result, and an ASCII skeleton identical to
the original's. The third is what makes it a repair and not a substitution -- it
can only ever change non-ASCII characters, so a truncation, a different block or
a shifted offset all leave the original alone, markers and all. Clean text is
untouched, not even reassembled, and a repaired page reports
`accents_repaired: {blocks, recovered, how}` because the text is no longer
byte-for-byte what the server sent.
Default ON because the alternative is shipping text measured to be corrupt. Set
false to see exactly what the server sent.
OPTION 2, prepared: a `coderai-vllm-ocr` pod image pinned to vLLM 0.20.1 -- the
version surya-ocr itself spawns (surya/settings.py:96) -- so the model can be
served on the stack its authors validate, the repair can be checked against a
reference rather than trusted, and the upstream bug report has a known-good
comparison. Selected per model with `runpod.engine: "coderai-vllm-ocr"`, built
with `build_capability_image.sh vllm-ocr`.
The pin set was verified to exist before the profile was written, because a
profile that cannot build is worse than none: wheels.vllm.ai/0.20.1/cu129 is
live, and torch 2.11.0 / torchvision 0.26.0 / torchaudio 2.11.0 (vLLM 0.20.1's
own metadata pins) all have +cu129 cp313 wheels. Qwen3_5ForConditionalGeneration
is in 0.20.1's registry, so it can serve the model. No torchcodec pin: 0.20.1
does not require one, and pinning a package nothing needs is its own hazard --
the file says what to add if a build ever surprises us. NOT a drop-in for
`coderai-vllm`: everything else on it would be pinned back seven minor releases.
tests: test_ocr_accent_repair.py, 29 tests -- the repair itself, every guard, and
the image profile. 833 passed across the ocr/runpod/config/admin/front selection
with the same 8 pre-existing failures.
Co-Authored-By:
Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PxYHWbCAjjjD7A1rhFKtaW
Showing
This diff is collapsed.
Please
register
or
sign in
to comment