Skip to content

tesseract://

OCR via the Tesseract open-source engine. Producer-only — feed it image / PDF bytes, receive extracted text on the body.

Two backends, picked by mode=:

Mode What runs When to use
cli (default) Local tesseract binary, shelled out via os/exec. Pure-Go from Helix's side. Single host, you control the install. Fastest setup — apt install tesseract-ocr / brew install tesseract and you're done.
http A Tesseract HTTP wrapper (hertzg/tesseract-server, otiai10/ocrserver, or any compatible variant). Shared OCR service, sidecar deployment, or you don't want the tesseract binary on the Helix host. Auto-detects JSON / plaintext responses.

No CGO. We deliberately did NOT pick otiai10/gosseract/v2 (the canonical CGO binding) — it would force every Helix build to link libtesseract, break CGO_ENABLED=0 static binaries, and complicate cross-compilation. The CLI gets the same engine with none of the build-system drag, and Tesseract's CLI has been stable since 3.x.

Companion: pkg/component/tesseract.

URI shape

tesseract://?<param>=<value>…                     # CLI mode
tesseract://<host>:<port>?<param>=<value>…        # HTTP mode (inferred)
tesseract://?mode=http&endpoint=http://ocr:8884   # HTTP mode (explicit)

When a host segment is present and mode= is not set, the component infers mode=http and uses the host as the endpoint. Pure-param form (tesseract://?lang=eng) stays in cli mode.

When to use this vs. rag-ingest

Need Use
OCR inside a RAG pipeline (ingest → chunk → embed → store) rag-ingest reader: ocr backend: tesseract
Call tesseract as a tool from an agent tesseract://
OCR an image mid-route (route is NOT a RAG pipeline) tesseract://
Need PDF page-splitting / parallel page OCR rag-ingest reader: ocr backend: tesseract page_split: true

Body shape

Request:

  • []byte → uploaded (HTTP mode) or written to a temp file (CLI mode).
  • string → treated identically to []byte.
  • io.Reader → consumed.

Helix sniffs magic bytes to pick a file extension (.png, .jpg, .pdf, .gif, .webp, .bmp, .tiff) so the Tesseract CLI's leptonica loader picks the right decoder.

Response:

  • Body: extracted text as a string.
  • HelixTesseractMode header: cli or http.
  • HelixTesseractLang header: the language(s) used.

Parameters

Connection

Param Type Default Description
mode enum cli cli or http.
endpoint string from URI host (HTTP mode only) Tesseract HTTP service base URL.
path string /tesseract (HTTP mode) HTTP path appended to endpoint. Use /ocr for otiai10/ocrserver, /tesseract for hertzg.
apiKey string — Bearer token sent as Authorization header (HTTP mode only).
timeout duration 2m Per-call timeout. Accepts Go duration (30s, 2m) or bare seconds.

Tesseract knobs

Apply to both modes.

Param Type Default Description
lang string eng -l value. Combine with +: eng+fra+deu. Requires the matching tessdata language files installed.
psm integer 3 Page Segmentation Mode (--psm). 0=OSD only, 3=fully automatic, 6=uniform block, 7=single line, 11=sparse text, 12=sparse with OSD, 13=raw line. See tesseract --help-psm.
oem integer (unset) OCR Engine Mode (--oem). 0=legacy, 1=LSTM only (recommended), 2=legacy+LSTM, 3=default. Omit to use the binary's default.

CLI-mode only

Param Type Default Description
binary string tesseract (via $PATH) Explicit path to the tesseract binary.
tessdataPrefix string (inherits) Overrides $TESSDATA_PREFIX. Useful when language data lives outside the system default (e.g. a CI cache mount).

HTTP response auto-detection

mode=http reads the response and tries known JSON shapes before falling back to plaintext:

Shape Server
{"data":{"stdout":"text",...}} hertzg/tesseract-server
{"result":"text"} otiai10/ocrserver
{"text":"..."} / {"stdout":"..."} / {"markdown":"..."} / {"content":"..."} other common wrappers
Raw body plain-text response

You don't configure this — it just works. The same decoder powers the rag-ingest backend: tesseract path so JSON envelopes never leak into embeddings.

Examples

CLI mode — OCR a PNG from disk

workflows:
  - id: ocr-incoming-image
    trigger:
      uri: file:///var/scans?move=.done&include=.*\.png
    steps:
      - target: tesseract://?lang=eng&psm=6
      - target: log://ocr-result

The file:// trigger streams each PNG body into the tesseract step; the local binary OCRs it; the resulting text lands in the log.

HTTP mode — point at a tesseract-server sidecar

workflows:
  - id: ocr-via-http
    trigger:
      uri: http-server://0.0.0.0:8080/ocr
    steps:
      - target: tesseract://ocr-sidecar:8884?lang=eng+deu

Posts every uploaded body to the sidecar. The component auto-detects the response JSON shape.

RAG ingest — full pipeline

workflows:
  - id: ingest-scanned-pdfs
    trigger:
      uri: file:///var/intake/pdfs?include=.*\.pdf
    steps:
      - rag-ingest:
          reader: ocr
          backend: tesseract
          endpoint: http://ocr-sidecar:8884
          language: eng
          page_split: true            # pdfcpu splits → parallel OCR
          concurrency: 4
      - rag-chunk:
          strategy: recursive
          chunk_size: 800
      - rag-embed:
          provider: openai
          model: text-embedding-3-small
      - rag-store:
          backend: qdrant
          url: ${QDRANT_URL}
          collection: scanned-pdfs

When endpoint: is omitted the rag-ingest path drops to the local CLI exactly like the standalone component does — no service needed.

Failure modes

Error Cause Fix
tesseract not found on PATH — install Tesseract OCR (apt-get install tesseract-ocr / brew install tesseract) mode=cli and the tesseract binary isn't reachable. Install it, or set binary= to an explicit path.
tesseract cli: exit status 1 (stderr: Error opening data file …) Language data missing for lang=. Install the matching tessdata package (tesseract-ocr-deu, etc.) or set tessdataPrefix=.
status 500: ... (HTTP mode) Service returned a non-200. The body snippet is preserved in the error. Check the service logs; verify multipart file field is what the wrapper expects.
psm N out of range (0-13) Invalid psm= value. Use 0-13 per tesseract --help-psm.
unknown parameter "x" Typo in a URI param. The describable surface above lists every accepted key.

Performance notes

  • CLI mode forks per call. Fine for hundreds of pages/min; for thousands/min run a mode=http sidecar so the model stays warm.
  • PDF inputs. Tesseract opens PDFs via leptonica (one page at a time). For multi-page PDFs the rag-ingest path with page_split: true parallelises better than a single tesseract call.
  • Language combinations cost time. eng+fra+deu is roughly 1.5–2× slower than eng. Only list languages you actually need.