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¶
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 multipartfilesfield, filename fromHelixDoclingFilenameheader (defaults toinput.pdf).string→ treated identically to[]byte.io.Reader→ streamed into the multipart body.- empty +
HelixDoclingSourceURLheader → docling-serve fetches the URL itself (avoids streaming large files through Helix).
Response:
- Body: rendered output as a
string. The format matchesoutput=(see below). Foroutput=json, the body is a JSON string literal — parse viaread(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:
5. Selective page extraction¶
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.
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 logConnections to remote services is only allowed when set explicitlyand the worker will fail unless docling-serve was started with--enable-remote-services(CLI flag) orDOCLING_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 anySourceHTTPfetch 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-doclingpinned tov0.1.2 - Vendor strategy: pin the version; fork into
internal/doclingif upstream stalls.
See also¶
rag-ingest— RAG-pipeline reader (backend: docling)http://— the underlying HTTP transport docling-serve speaks