• Stefy Lanza (nextime / spora )'s avatar
    ocr: the accents were never lost by the model, so take them back from its bytes (v0.3.28) · a1897d77
    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: 's avatarClaude Opus 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01PxYHWbCAjjjD7A1rhFKtaW
    a1897d77
vllm-ocr.venv-vllm.txt 1.65 KB