Skip to content

feat(ocr): add the PaddleOCR-VL provider (paddleocr-vl) - #147

Closed
chadtalsma-svg wants to merge 2 commits into
happyvertical:mainfrom
chadtalsma-svg:claude/paddleocr-vl-146
Closed

chadtalsma-svg wants to merge 2 commits into
happyvertical:mainfrom
chadtalsma-svg:claude/paddleocr-vl-146

Conversation

@chadtalsma-svg

Copy link
Copy Markdown
Contributor

Adds a paddleocr-vl OCR provider that sends images to a PaddleOCR-VL /layout-parsing service. That's the same service @happyvertical/pdf reads PDFs through (happyvertical/pdf#142; the reader provider is in happyvertical/pdf#146), so a consumer's images and PDFs can use one model.

  • Request: one POST per image page with fileType: 1, layout detection on. Results are joined in order. No retries.
  • Formats:
    • PNG and JPEG are sent unchanged.
    • TIFF is sent one request per page, in order (multi-page scanner output).
    • WebP, GIF and BMP are converted to PNG with sharp, first frame only, so an animated GIF isn't sent once per frame.
    • Raw pixels must be 8-bit, 1 to 4 channels, with a byte length of width × height × channels; they are converted to PNG.
    • Unknown or undecodable input is refused before any request. Remote image URLs are refused, never fetched.
  • Native addon: sharp loads only when a conversion is needed, through an opaque specifier (the factory's loadNodeOCRModule pattern), so no bundler can resolve sharp from the root entry. test:vite-consumer passes.
  • Explicit only: it isn't in the auto priority list, because images leave the process. If it's named but unconfigured, the factory throws; it never falls back silently.
  • Settings: providerConfig['paddleocr-vl'] takes { baseUrl, apiKey, timeout }, falling back to HAVE_OCR_PADDLEOCR_* and then HAVE_PDF_PADDLEOCR_*, so one setting serves both packages.
  • Timeout: 65 minutes by default, which covers a cold start. OCROptions.timeout is ignored, because the factory fills it from HAVE_OCR_TIMEOUT, a limit sized for local engines.
  • Errors never include the service's response or document text.

Validation: these all pass:

  • pnpm lint
  • docs:api:check
  • typecheck
  • build
  • test:coverage: 139 tests. Coverage is 84.1/74.5/89.2/85.4 against the 80/65/80/80 gate; paddleocr-vl.ts is at 94/91/91/96.
  • test:vite-consumer

The 14 new contract tests run against a local HTTP stub and use real PNG, JPEG, multi-page TIFF, WebP and animated GIF inputs made with sharp. Live GPU parsing is checked downstream in teamworksfab/teamworks-os.

Independent review: Claude Opus 5.5, standard risk, two passes.

  • Pass 1 found one blocker (a literal import('sharp') reached the root bundle) and three should-fix issues (animated frames were expanded, 16-bit raw pixels were accepted, raw buffers could be misread as BMP or GIF). All are fixed in 0b89579.
  • Pass 2 was CLEAN. It also confirmed that sharp resolves at runtime from the built package.

Closes #146

🤖 Generated with Claude Code

chadtalsma-svg and others added 2 commits October 16, 2026 07:44
paddleocr-vl sends each image page to a PaddleOCR-VL /layout-parsing
service (fileType 1). PNG and JPEG pass through; TIFF (every page),
WebP, GIF, BMP and raw pixels are converted to PNG with sharp, loaded
only when needed. Explicit only, never in auto; no retries; errors never
echo the response. Settings fall back from HAVE_OCR_PADDLEOCR_* to
HAVE_PDF_PADDLEOCR_*.

Closes happyvertical#146

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…appyvertical#146)

Load sharp through an opaque specifier so bundlers never resolve it,
send only the first frame of an animated GIF or WebP (only TIFF pages
are document pages), refuse raw pixels that are not 8-bit or do not
match their declared size, and treat declared raw pixels as raw before
sniffing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@willgriffin

Copy link
Copy Markdown
Contributor

Reviewed and integrated with original authorship into #148. Fixed actual BMP decoding, corrupt PNG/JPEG refusal, sanitized local read errors, one-response-page validation, and blank-page detection positions. All 143 tests, coverage, packed Vite and public-API/codec consumer checks pass; independent full review plus recall is CLEAN. #148 will supersede this PR after green CI, merge queue and release.

@willgriffin

Copy link
Copy Markdown
Contributor

The main provider contribution and initial fixes shipped through #148 in v0.62.0. The accepted orientation/unused-image review fixes are being delivered as follow-up #149. Original authorship was preserved.

@chadtalsma-svg
chadtalsma-svg deleted the claude/paddleocr-vl-146 branch October 28, 2026 16:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add a PaddleOCR-VL provider (paddleocr-vl) for images, multi-page TIFF included

2 participants