Stu Mason · Folkestone, Kent
One entryMerged PR· crunch.stumason.dev· PHP

PR #24 merged: Async batch OCR: POST /ocr/batch for roll packs (#14)

additions
+461
deletions
-82
files changed
10

Closes #14.

What

POST /ocr/batch — OCR many crops in one async job instead of forcing the client to fire N strictly-sequential /ocr calls (each ~8s) and hand-throttle around the LimitInflight 429s.

Mirror /transcribe:

  1. POST /ocr/batch with { "crops": [ {"box": [x,y,w,h], "image": "<base64>"}, … ] } (1–200 crops) → 202 job.
  2. Poll GET /jobs/{id} until completed.
  3. result.results is an array (submission order) of {box, text, error}box echoed back verbatim so you can map each result to its source; error is non-null only for crops that failed (one bad crop never sinks the pack).

Why it satisfies "drain within the inflight cap"

The submit is cheap (persist + queue), so it isn't under LimitInflight. The actual OCR runs in OcrBatchJob on the single queue worker, and the vision sidecar is serial — so crops OCR one at a time and the batch never blows the cap the way client-side fan-out does.

Notable

  • /jobs/{id} is now type-aware. It previously hard-coded the transcribe verbose_json shape; a batch result would have been mangled. Extracted JobPresenter (branches on type) and moved polling to a generic JobController. /transcribe output is unchanged.
  • Crops are validated + persisted up front — a malformed crop is a fast 422 before any job is queued.
  • README capability table updated (also adds the /ocr + /detect rows that shipped earlier but were never documented).

Tests

tests/Feature/OcrBatchTest.php: queues+202 envelope, per-crop {box,text} mapping & temp cleanup, per-crop error isolation, type-aware polling, validation (empty/oversized/invalid-base64), auth.

Follow-ups (not in scope)

  • The OCR engine itself (#13) — benchmarked separately; recommendation incoming. This PR is transport only; it'll pick up engine= for free once #13 lands.
  • Optional hardening: retry only the failed crops rather than relying on per-crop error capture.

🤖 Generated with Claude Code