-
Stefy Lanza (nextime / spora ) authored
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
a1897d77