Skip to content

docling://

Document parser (PDF, DOCX, PPTX, HTML, image) that delegates to IBM / Red Hat's docling-serve. Producer-only — feed it bytes (or a URL via header), receive parsed markdown / JSON / HTML / doctags on the body.

Built on github.com/iguanesolutions/go-docling@v0.1.2. No hand-rolled multipart, no openapi codegen — the upstream Go client carries the typed surface (functional options, format enums, async task polling). We pin the version; if upstream goes stale we fork (replace github.com/iguanesolutions/go-docling => internal/docling).

URI shape

docling://<host>:<port>?<param>=<value>…
docling://?endpoint=http://docling:5001&<param>=<value>…

Either form works. The path-segment form is the canonical Helix style; the ?endpoint=… form is handy when the URL is templated.

When to use this vs. rag-ingest

Need Use
Parse PDF inside a RAG pipeline (ingest → chunk → embed → store) rag-ingest type: ocr backend: docling
Call docling as a tool from an agent docling://
Parse a document mid-route (route is NOT a RAG pipeline) docling://
Need async / job polling docling://?operation=convert-async
Need chunk/hybrid in one shot (not yet — only convert is wrapped)

Body shape

Request:

  • []byte → uploaded as multipart files field, filename from HelixDoclingFilename header (defaults to input.pdf).
  • string → treated identically to []byte.
  • io.Reader → streamed into the multipart body.
  • empty + HelixDoclingSourceURL header → docling-serve fetches the URL itself (avoids streaming large files through Helix).

Response:

  • Body: rendered output as a string. The format matches output= (see below). For output=json, the body is a JSON string literal — parse via read(body()) in Starlang.

Parameters

Connection

Param Type Default Description
endpoint string from URI host docling-serve base URL.
apiKey string — Bearer token (Authorization header).
timeout duration 5m Overall HTTP timeout. Accepts Go duration (600s, 5m) or bare seconds.

Operation

Param Type Default Description
operation enum convert convert (sync, body returns rendered output) or convert-async (returns HelixDoclingTaskID for polling).

Output

Param Type Default Description
output enum md md / json / html / html_split_page / text / doctags.
imageExportMode enum docling default embedded / placeholder / referenced.
imagesScale number docling default Resolution multiplier for embedded images (e.g. 2.0).
includeImages boolean true Include images in the output.
mdPageBreakPlaceholder string — Page-break marker injected into markdown output.
pageRange string all [start, end] pair (1-based). docling enforces exactly 2 elements. Examples: 1-5, 7, 1,5. Disjoint multi-ranges (1-3,5-7) are NOT supported by docling-serve.
fromFormat enum auto-detect Force source format (pdf/docx/pptx/html/image/md/csv/xlsx/asciidoc/xml_uspto/xml_jats/mets_gbs/json_docling/audio). Empty = auto-detect from filename/magic.

OCR & parsing

Param Type Default Description
ocr boolean false Enable OCR for scanned PDFs / images.
forceOCR boolean false OCR even pages with extractable text.
ocrEngine enum easyocr easyocr / tesseract / tesserocr / rapidocr / ocrmac.
ocrLang string — Comma-separated language hints (e.g. en,de).
pdfBackend enum dlparse_v4 dlparse_v4 / dlparse_v2 / dlparse_v1 / pypdfium2.
tableMode enum accurate accurate (slower, better tables) or fast.
tableCellMatching boolean false Match table cells to structure (slower, better cell alignment).
pipeline enum standard standard / vlm / asr. vlm uses a vision LLM.
doTableStructure boolean true Parse table cell structure.
pictureDescriptionAreaThreshold number 0.0 Min image area (0.0–1.0 fraction of page) before captioning kicks in. Raise to skip small icons.
doCodeEnrichment boolean false Detect + format code blocks.
doFormulaEnrichment boolean false Extract math regions as LaTeX.
doPictureClassification boolean false Classify pictures (figure / chart / diagram).
doPictureDescription boolean false Caption pictures (requires VLM config on docling-serve).
documentTimeout integer — Per-document timeout (seconds) enforced inside docling.
abortOnError boolean false Fail the request on any document-level error.

VLM pipeline (pipeline=vlm)

What it is. Standard PDF parsing assembles text from PDF stream ops + heuristic layout analysis. The VLM pipeline takes a different approach: docling renders each page to an image and feeds it to a multimodal LLM that outputs structured text (markdown / doctags / OTSL tables). This trades latency + cost for accuracy on tricky layouts — multi-column papers, scientific figures, handwritten notes, medical forms.

Three mutually-exclusive selectors. Pick one:

Mode What runs the VLM When to use it
Built-in (vlmPipelineModel=…) A model bundled with docling-serve Quickest path. Pick granite_vision for general docs, smoldocling for low-memory hosts.
Local repo (vlmPipelineModelRepoID=…) A HuggingFace model loaded into docling-serve's process You want a specific HF model that isn't bundled (e.g. a fine-tuned Granite, Qwen2-VL, etc.). docling-serve needs the GPU + weights downloaded.
External API (vlmPipelineModelURL=…) A separate VLM service (OpenAI-compatible chat-completions endpoint) docling-serve has no GPU, OR you already pay for GPT-4o / Claude 3.5 Sonnet / an internal Granite-Vision-on-vLLM and want docling to delegate to it.

Precedence when multiple are set: local repo > external URL > built-in.

Selector params

Param Type Default When to set it
vlmPipelineModel enum — Use built-in mode. Values: smoldocling, smoldocling_vllm, granite_vision, granite_vision_vllm, granite_vision_ollama, got_ocr_2.
vlmPipelineModelRepoID string — Use local-repo mode. HuggingFace id like ibm-granite/granite-vision-3.1-2b-preview.
vlmPipelineModelURL string — Use external-API mode. Full URL of an OpenAI-compatible chat-completions endpoint (e.g. https://api.openai.com/v1/chat/completions or your internal vLLM at http://vllm:8000/v1/chat/completions). docling-serve sends each page image + the configured prompt to this URL.

Shared params (apply to whichever selector you picked)

Param Type Default Why you'd set it
vlmPipelineModelResponseFormat enum required for local + API Tell the VLM what to return: markdown (most readable), doctags (docling's structured tagging), html, otsl (table-only format), plaintext. Built-in models have a baked-in default.
vlmPipelineModelPrompt string "Convert this page to docling." Override the VLM instruction. Use to inject domain rules — "Preserve every dosage, route, and frequency verbatim. Render tables as structured Markdown." for clinical docs, "Skip page numbers and footnotes." for academic papers, "Ignore decorative borders and watermarks." for legal scans.
vlmPipelineModelScale number 2.0 Image scale before sending to the VLM. 1.0 = page resolution, 2.0 = 2× upscale (sharper, larger payload). Raise to 3.0–4.0 for small-print medical forms; drop to 1.0 if you're hitting token-cost limits.

Mode-specific params

Param Mode Default Why you'd set it
vlmPipelineModelInferenceFramework local required mlx (Apple Silicon), transformers (NVIDIA GPU + HF transformers), vllm (vLLM-backed serving inside docling).
vlmPipelineModelTransformersModelType local automodel Forces the HF AutoModel* class. Use automodel-vision2seq for image-to-text models that the auto-loader misclassifies, automodel-imagetexttotext for newer multimodal variants. Leave default unless you're loading an exotic model.
vlmPipelineModelTimeout API 60 Per-page timeout in seconds. Raise for slow remote VLMs (rate-limited tiers, large reasoning models).
vlmPipelineModelConcurrency API 1 Pages processed concurrently. Raise to 4–8 when your VLM API has burst capacity; keep at 1 for strict rate limits.

Picture description (companion to doPictureDescription=true)

What it is. pipeline=vlm converts WHOLE PAGES via a VLM. Picture description is narrower — keep pipeline=standard, but ALSO caption each detected figure / chart / diagram inline. Caption text goes under the image in the markdown output, so downstream RAG retrieval can hit figures via their caption.

Why you'd turn it on: medical reports with embedded waveforms or imaging studies, scientific papers with figures whose alt-text matters, internal slide decks where the message lives in the chart.

Two mutually-exclusive captioners. API mode wins if both URL and RepoID are set:

Param Mode Default When to set it
pictureDescriptionRepoID local — HuggingFace id for an image-captioning model (e.g. Salesforce/blip-image-captioning-large). docling-serve must have GPU + weights.
pictureDescriptionURL API — External captioning endpoint (OpenAI-compatible chat with image input). Use to delegate to GPT-4o, Claude, internal LLaVA, etc.
pictureDescriptionPrompt both "Describe this image in a few sentences." Override for domain context. Healthcare: "Describe the clinical content of this image without speculation. State 'unreadable' for blurred scans."
pictureDescriptionAreaThreshold both 0.0 Minimum image area (fraction of page, 0.0–1.0) before captioning kicks in. Default 0.0 captions every image; raise to 0.05 to skip small icons / logos and save VLM calls.
pictureDescriptionMaxNewTokens local 200 Caption length cap. Lower to 64 for terse captions, raise to 512 for descriptive ones.
pictureDescriptionDoSample local false true = sampled (more creative), false = greedy (deterministic). Keep false for reproducible RAG ingestion.
pictureDescriptionTimeout API 20 Per-call timeout (seconds).
pictureDescriptionConcurrency API 1 Concurrent caption calls.

Choosing between OCR, picture description, and full VLM pipeline

These three are easy to confuse — they target different problems:

Problem Setting What it does
Scanned PDF, no extractable text ocr=true OCR engine runs per page, emits plain text. Cheap. No layout understanding.
Born-digital PDF, but the figures need captions for RAG doPictureDescription=true + pictureDescriptionURL=… Keep the fast standard pipeline; only invoke a VLM for figure crops.
Born-digital PDF, but the LAYOUT is too complex for heuristic parsing (multi-column, footnotes interleaved, dense tables) pipeline=vlm + vlmPipelineModel=granite_vision Whole-page VLM rendering. Most accurate, most expensive.

You can combine ocr=true with pipeline=vlm (OCR runs first, VLM sees both the image AND the OCR text) — useful for scanned medical forms where dosage numbers must be preserved exactly.

Headers

Header Direction Purpose
HelixDoclingOperation Read Per-request operation override (convert / convert-async).
HelixDoclingSourceURL Read URL for docling-side fetch (skip the multipart upload).
HelixDoclingFilename Read Filename for the multipart upload. Default input.pdf.
HelixDoclingTargetURL Read (Future) PUT destination URL when target=put.
HelixDoclingStatus Written success / partial_success / failure / skipped / pending (async).
HelixDoclingProcessingTime Written Seconds spent in docling.
HelixDoclingTaskID Written Async-mode task identifier (poll out-of-band).
HelixDoclingOutputFormat Written Echoes the resolved output format.
HelixDoclingErrors Written []string of component_type/module_name: message entries when status != success.

Examples

1. Convert a PDF dropped into a watched folder

- id: ingest-pdfs
  from: file:///var/data/in?include=.*\.pdf$&delete=true
  steps:
    - to: docling://docling:5001?ocr=true&tableMode=accurate&output=md
    - to: log://?level=info

2. Use as an agent tool

tools:
  - id: parse_doc
    description: "Parse a PDF/DOCX/PPTX into markdown."
    input:
      url: { type: string, description: "Document URL", required: true }
    steps:
      - set-header:
          name: HelixDoclingSourceURL
          starlang: input.url
      - to: docling://docling:5001?ocr=true

3. Async conversion with task polling

- id: big-pdf
  from: direct://convert-bigpdf
  steps:
    # 1. Submit asynchronously.
    - to: docling://docling:5001?operation=convert-async&ocr=true
    # 2. HelixDoclingTaskID is now on the headers; persist + return.
    - transform:
        starlang: |
          %sl 2.0
          output application/json
          input headers
          ---
          { "task_id": headers["HelixDoclingTaskID"], "status": "submitted" }

4. Replace a hand-rolled HTTP call

Before — 7 lines of http:// + headers + remove-headers:

- remove-headers: { pattern: HelixHttp* }
- set-header: { name: Content-Type, constant: multipart/form-data }
- to: http://docling:5001/v1/convert/file?method=POST&timeout=600s
- transform:
    starlang-file: scripts/extract_markdown.sl

After — one line:

- to: docling://docling:5001?output=md&timeout=600s

5. Selective page extraction

- to: docling://docling:5001?pageRange=1-1&output=json

6. Built-in VLM pipeline (no external service)

Use docling's bundled granite_vision model to re-parse pages via a vision LLM. Best for complex multi-column layouts where heuristic parsing produces jumbled reading order.

- to: docling://docling:5001?pipeline=vlm&vlmPipelineModel=granite_vision&output=md&timeout=10m

docling-serve must be running with the granite-vision weights loaded (check its startup config / docker-compose env).

7. External VLM — delegate to GPT-4o for layout understanding

You don't run a VLM inside docling-serve; instead docling sends each page image to your OpenAI-compatible endpoint and uses the response. Cheaper to operate (no GPU on docling host) but pay-per-token at the VLM provider.

⚠️ Operator prerequisite — --enable-remote-services. docling-serve refuses all outbound VLM / picture-description calls by default as an SSRF safeguard. Helix will pass your URL through correctly, but docling will log Connections to remote services is only allowed when set explicitly and the worker will fail unless docling-serve was started with --enable-remote-services (CLI flag) or DOCLING_SERVE_ENABLE_REMOTE_SERVICES=true (env var). This is a docling-serve config — Helix can't override it client-side.

Same prerequisite applies to all external-API params: vlmPipelineModelURL, pictureDescriptionURL, and any SourceHTTP fetch with auth headers.

- to: |
    docling://docling:5001
    ?pipeline=vlm
    &vlmPipelineModelURL=https://api.openai.com/v1/chat/completions
    &vlmPipelineModelResponseFormat=markdown
    &vlmPipelineModelTimeout=120
    &vlmPipelineModelConcurrency=4
    &vlmPipelineModelScale=2.0
    &output=md
    &timeout=15m

URL-encode &s when inlining the URI. The cleaner form uses params::

- to: docling://docling:5001
  params:
    pipeline: vlm
    vlmPipelineModelURL: https://api.openai.com/v1/chat/completions
    vlmPipelineModelResponseFormat: markdown
    vlmPipelineModelTimeout: 120
    vlmPipelineModelConcurrency: 4
    vlmPipelineModelScale: 2.0
    output: md
    timeout: 15m

Authentication for the VLM API is configured on the docling-serve side (typically env vars like OPENAI_API_KEY mounted into the container) — Helix passes the URL only. Helix's apiKey= is for docling-serve's own auth, not for the downstream VLM.

8. Custom VLM prompt for clinical PDFs

Both the whole-page VLM pipeline AND the picture-description captioner take a prompt override. Useful for healthcare to preserve dosages and suppress hallucination. The prompt is shared local / API.

- to: docling://docling:5001
  params:
    pipeline: vlm
    vlmPipelineModel: granite_vision
    vlmPipelineModelResponseFormat: markdown
    vlmPipelineModelPrompt: |
      Convert this medical document page to clean Markdown.
      Rules:
      - Preserve every drug name, dosage, route, and frequency verbatim.
      - Render dosage tables as proper Markdown tables (no flattening).
      - Use Markdown headings for section titles (## for top-level).
      - Do NOT summarise. Do NOT paraphrase.
      - If a section is unreadable, write [UNREADABLE] in its place.
    output: md

9. Picture description (caption figures only, keep standard parsing)

Standard pipeline is fast and accurate for text — pay the VLM cost ONLY on detected figures. Captions land inline in the markdown so RAG retrieval can hit "figure showing waveform after treatment" even though the original PDF only had alt-less images.

- to: docling://docling:5001
  params:
    output: md
    doPictureDescription: "true"
    pictureDescriptionURL: https://api.openai.com/v1/chat/completions
    pictureDescriptionPrompt: |
      Describe the clinical content of this image in 1-2 sentences.
      Avoid speculation. Write "unreadable" if blurred.
    pictureDescriptionAreaThreshold: "0.05"   # skip tiny icons / logos
    pictureDescriptionTimeout: "30"
    pictureDescriptionConcurrency: "4"

10. Combine OCR + VLM (scanned forms with critical numbers)

For scanned medical forms where the layout is complex AND dosage numbers must be exact, run BOTH: OCR extracts text precisely, VLM reconstructs the page structure with that text as grounding.

- to: docling://docling:5001
  params:
    pipeline: vlm
    vlmPipelineModel: granite_vision
    vlmPipelineModelResponseFormat: markdown
    vlmPipelineModelPrompt: "Preserve all numbers exactly. Use OCR text where present; do not re-OCR."
    ocr: "true"
    forceOCR: "true"
    ocrEngine: tesseract
    ocrLang: "en"
    output: md
    timeout: 20m

Errors

Trigger Error shape
docling-serve returns 4xx/5xx docling: convert: http <code>: <body preview>
status: failure in 200 response docling: conversion status=failure + HelixDoclingErrors header
Empty body and no HelixDoclingSourceURL docling: empty body (set body to file bytes, or HelixDoclingSourceURL header for a remote fetch)
Network timeout wrapped Go net/http timeout error

The 200-with-failure-status case is treated as an error so route-level onException blocks can catch it; the rendered md_content (often empty in failure cases) is still written to the body for diagnostics, and HelixDoclingErrors carries the per-component messages.

Source

  • Component: pkg/component/docling/
  • Upstream client: github.com/iguanesolutions/go-docling pinned to v0.1.2
  • Vendor strategy: pin the version; fork into internal/docling if upstream stalls.

See also

  • rag-ingest — RAG-pipeline reader (backend: docling)
  • http:// — the underlying HTTP transport docling-serve speaks