Doc Chunker
Convert audio, video, PDFs, images, and Office documents into text chunks for your vector stores.
Doc Chunker is an optional DeepFellow component that turns diverse knowledge sources into clean, searchable text. It converts documents, presentations, spreadsheets, images, audio, and video into text and splits that text into chunks ready for embedding.
The connection between Doc Chunker and vector stores is still under development.
This integration is active by default: doc_chunker_use defaults to true, so DeepFellow Server
sends vector store files to Doc Chunker unless you turn it off. If you don't accept the status of
this integration, set doc_chunker_use to false to use the built-in text extraction. It reads
pdf, docx, pptx, md, rtf, and plain text, and it returns flat text with no layout, no OCR,
no image descriptions, and no transcription. See Doc Chunker Settings.
When Doc Chunker is active, DeepFellow converts your files, splits them into chunks, and embeds those chunks for search. Retrieve the original text of any chunk, its neighboring context, or the whole reconstructed file with the chunk fragment endpoint.
What You Can Convert
Doc Chunker handles a wide range of formats.
- Documents and Markup:
pdf,docx,pptx,xlsx,html,md. OCR is enabled by default and extracts text from scanned PDFs and images embedded in documents. - Legacy Office formats:
doc,ppt,xls,odt,odp,ods,rtf. Doc Chunker converts these with LibreOffice before parsing. - Images:
png,jpg,jpeg,tiff,bmp. OCR reads text from the image. Optional image description adds a natural-language summary of the picture. - Audio:
mp3,wav,m4a,ogg,flac,opus,aac,wma. Doc Chunker transcribes speech to text through a speech-to-text model. - Video:
mp4,mkv,mov,avi,webm. Doc Chunker extracts the audio track and transcribes it.
Doc Chunker handles any other file type in two steps. First it tries to read the file as plain text.
If that succeeds, the text becomes a document whose paragraphs are split on blank lines. If it fails,
Doc Chunker falls back to Apache Tika, which is what happens with a format such as epub.
A file that reads as plain text, such as csv, txt, json, or yaml, never reaches Apache Tika
and never gets a structured layout. A csv file becomes flat text rather than a table, so its rows
and columns carry no structure into the chunks.
Audio and video transcription requires a speech-to-text model. Image descriptions require a vision-language model. Both reach the model through an inference gateway that you configure on the container. See Inference Gateway.
Install Doc Chunker
Install Doc Chunker from the Infra Web Panel as a model of the custom service.
In the services view, locate the custom service and open model list.
Find the doc_chunker model and open its configuration form.
Set the required fields: endpoint prefix, hardware, and the inference gateway URL.
The default prefix is doc_chunker, which exposes Doc Chunker at /custom/doc_chunker. The gateway
URL, INFRA_API_URL, arrives prefilled with http://infra:8086, the address of the gateway in a
standard deployment.
Set the environment variables you need, described in Container Configuration.
The defaults work for every format. Image descriptions and audio transcription need one more value,
INFRA_API_KEY, and only when your gateway requires a key.
Leave "Docker image version" empty to install the image DeepFellow ships with your Infra release. Set it to a tag to pin a different image.
Leave the proxy timeout empty to use the global standard_proxy_timeout_seconds, which is 600
seconds. Raise it for large files: a conversion that outlives the timeout fails even though Doc
Chunker is still working on it. See Proxy Timeout.
Click "Install". DeepFellow selects the CPU or GPU image automatically based on your hardware.
After a while, the model will appear with the green "Installed" label.
The GPU image needs about 15 GB of disk space and runs much faster. The CPU image needs about 4 GB. The form declares 15 GB for both, so the figure it shows for the CPU image is too high.
doc_chunker_use defaults to true, so DeepFellow Server routes vector store files to Doc Chunker
whether or not the model is installed. With no model behind it, the endpoint prefix resolves to
nothing, and the Infra gateway answers 400 with
{"error": {"message": "Given url is not supported", "type": "invalid_request_error"}}.
POST /v1/vector_stores/{vector_store_id}/files still answers 200, because it only queues the
file. Ingestion runs in the background, so the failure surfaces on the file itself: its status
becomes failed, and last_error carries the gateway's message. The synchronous paths,
GET /v1/files/{file_id}/text and POST /text_converter/text, return the error directly.
Install Doc Chunker, or set doc_chunker_use to false.
Doc Chunker Settings
Installing the model exposes the Doc Chunker endpoint. DeepFellow Server reads the following settings to route vector store files through it.
Doc Chunker itself holds no model configuration, and every request names the model it needs. These settings are what DeepFellow Server puts into that request.
doc_chunker_use: Whether to send files to Doc Chunker when you add them to a vector store. Set it tofalseto keep the built-in text extraction. Defaulttrue.doc_chunker_prefix: Endpoint prefix the server uses to reach Doc Chunker. It must match the prefix you set when you installed the model. Defaultdoc_chunker.doc_chunker_picture_description_model: Vision-language model the server sends as themodelparameter when it asks Doc Chunker to describe pictures. Defaultgranite3.2-vision:2b.
All three are dynamic settings, so a change applies immediately and never needs a restart:
deepfellow server config set doc_chunker_use=true doc_chunker_prefix=doc_chunkerThe Doc Chunker section of the Server Web Panel holds the first two. To change
doc_chunker_picture_description_model, use PUT /admin/config.
The matching environment variables, DF_DOC_CHUNKER_USE, DF_DOC_CHUNKER_PREFIX, and
DF_DOC_CHUNKER_PICTURE_DESCRIPTION_MODEL, only seed these settings on the server's first start.
After that the values live in the server's configuration file, and setting the environment variable
will have no effect. Use the Server Web Panel or PUT /admin/config instead.
Container Configuration
Set these environment variables on the Doc Chunker model in the Infra Web Panel.
Configuration Options
The Doc Chunker installation form exposes the following fields.
Processing
| Variable | Description | Default |
|---|---|---|
MAX_CONCURRENT | Maximum number of files Doc Chunker converts in parallel. | 2 |
DOCUMENT_TIMEOUT | Maximum time in seconds for a single conversion. | 600 |
LIBREOFFICE_TIMEOUT | Maximum time in seconds LibreOffice gets to convert one legacy Office file. | 120 |
The defaults work for every format listed in What You Can Convert. Doc Chunker holds further timeouts for its Apache Tika and Docling backends, and the form leaves them at their defaults.
Chunk Size
A chunk holds at most MAX_TOKEN_COUNT tokens, which is 512. The install form doesn't expose this
variable, so every chunk keeps that budget.
Doc Chunker fills 70 percent of the budget, 358 tokens, with text unique to the chunk, and spends the remaining 30 percent on overlap. The overlap runs forward: Doc Chunker takes whole sentences from the start of a chunk and appends them to the chunk before it. The earlier chunk grows, and the later one stays as it was. Each overlap also stays within about 30 percent of the length of the chunk it extends, so a short chunk grows by far less than the 153-token ceiling.
Doc Chunker overlaps only consecutive chunks of running text, so a table never bleeds into its neighbors.
Chunk boundaries are measured with the sentence-transformers/all-MiniLM-L6-v2 tokenizer, which is
Docling's own default. The tokenizer is fixed, so its token counts differ from the ones your
embedding model counts. Matching the two is planned work.
The chunking_strategy parameter of the vector store endpoints applies only to the built-in token
chunker. Chunking through Doc Chunker takes no per-request parameters, so MAX_TOKEN_COUNT and the
tokenizer above decide every chunk boundary. Per-request control is planned work.
Inference Gateway
Image description and audio transcription both call an OpenAI-compatible inference gateway, so they
share one address and one key. Point INFRA_API_URL at the gateway root, not at a specific endpoint.
Doc Chunker appends /v1/chat/completions for image description and /v1/audio/transcriptions for
transcription.
| Variable | Description | Default |
|---|---|---|
INFRA_API_URL | Gateway root, for example http://ollama:11434. | |
INFRA_API_KEY | API key for the gateway, if it requires one. |
Without INFRA_API_URL, neither feature is available, and Doc Chunker rejects a request that needs
one rather than returning a document without it.
Image Descriptions
Image description is opt-in per request. Pass describe_images=true and name the vision-language
model with model. Both are required together: a request that asks for descriptions without a model
is rejected with 422, and so is a request naming a model the gateway doesn't offer.
Doc Chunker describes every picture the document holds. There is no size threshold, so icons and logos are described too.
| Variable | Description | Default |
|---|---|---|
PICTURE_DESCRIPTION_PROMPT | Prompt that controls how the model describes each image. | Describe this image in detail. |
PICTURE_DESCRIPTION_API_MAX_TIME | Maximum wait time in seconds for a single gateway call. | 300 |
PICTURE_DESCRIPTION_CONCURRENCY | Number of pictures described in parallel within one document. | 4 |
PICTURE_DESCRIPTION_CONCURRENCY bounds a single document, and Doc Chunker converts MAX_CONCURRENT
documents at a time. The gateway therefore sees up to the product of the two, which is 8 requests
with both at their defaults. Raise it for a gateway that takes the load.
Audio Transcription
For audio and video, transcription is the conversion: the transcript becomes the document's text.
Name the speech-to-text model per request with model. It is required, so uploading audio or video
without it is rejected with 422.
Doc Chunker splits the transcript on silence, and every piece keeps the time span it was spoken in.
A silence longer than AUDIO_SILENCE_THRESHOLD always ends a chunk, so no chunk ever spans a pause.
| Variable | Description | Default |
|---|---|---|
AUDIO_STT_MAX_TIME | Maximum wait time in seconds for a single speech-to-text call. | 600 |
AUDIO_SILENCE_THRESHOLD | Minimum duration of silence in seconds that splits the transcript. | 2.0 |
Converted Documents
When Doc Chunker is active, adding a file to a vector store with
POST /v1/vector_stores/{vector_store_id}/files converts the file through Doc Chunker first, and
the vector store stores the chunks that come back.
DeepFellow keeps the converted document next to the file, under the path set by
DF_FILESTORAGE_META_PATH, which defaults to storage/. Adding the same file again, or asking for
its text later, reuses that document instead of converting the file a second time. A conversion that
stopped halfway leaves nothing reusable behind, so the next request converts the file again.
POST /v1/metadata/expand, which extracts metadata fields from a document with an LLM, uses the
same cache. When a requested field is not present in a document's embedded metadata, DeepFellow
falls back to Doc Chunker to extract the document's text, converting the file if no conversion is
cached yet. To have a vision model describe images during that fallback conversion, set
image_processing_mode to description in the request body. The default value, ignore, skips
image descriptions. The setting applies only when the fallback runs.
Because the conversion is shared, whichever feature converts a file first decides whether the cached conversion holds image descriptions. If a metadata expansion job asks for descriptions but an earlier conversion produced the cached document without them, DeepFellow reuses the existing cache as is and logs a warning instead of reconverting. The same warning covers the reverse case, and the case where Doc Chunker is disabled and the fallback produces no descriptions at all.
Both GET /v1/files/{file_id}/text and POST /text_converter/text take an image_mode parameter.
It defaults to none on the first, and the second requires it. When it asks for descriptions, the
server names the vision-language model from doc_chunker_picture_description_model.
Besides none and descriptions, image_mode accepts embedded, which inlines each image as a
base64 data: URI. GET /v1/files/{file_id}/text also accepts referenced, which links each image
to /v1/files/{file_id}/assets/{name}. An ad-hoc conversion keeps no assets to link to, so
POST /text_converter/text doesn't offer that mode. Both modes need output_format=md and return
400 without it.
Image descriptions are written while the document is being converted, so a document converted
without them never gains them. On GET /v1/files/{file_id}/text the converted document is kept and
reused, so image_mode=descriptions produces descriptions on a file's first conversion only: a
later call asking for them returns the document as it was converted. POST /text_converter/text
keeps nothing, so it describes the images on every call that asks.
Call Doc Chunker Directly
For advanced use cases, call Doc Chunker through the /custom/doc_chunker endpoint without a vector
store. The two operations are separate endpoints, and conversion never happens implicitly:
POST /convert turns a file into a Docling document, and POST /chunk turns that document into
chunks.
Address these calls to DeepFellow Server with your project API key. The server proxies them to Doc Chunker and supplies the Infra credentials itself, so your client never holds the Infra API key. See Custom Endpoints.
Your project's custom_endpoints must allow the exact path you call. It defaults to all, which
allows every custom endpoint. If you restrict it, list full paths such as doc_chunker/convert. The
prefix alone doesn't match, so a list holding only doc_chunker answers 403 for
doc_chunker/convert. DeepFellow Server answers 403 before the request reaches Doc Chunker.
The list governs only the calls you address to /custom/doc_chunker/.... Vector store ingestion and
GET /v1/files/{file_id}/text reach Doc Chunker from inside the server, which doesn't consult
custom_endpoints. See Authorization.
Convert a File
POST /custom/doc_chunker/convert accepts multipart/form-data with the file in the file field,
and returns a ZIP archive named after the uploaded file.
| Parameter | Description | Default |
|---|---|---|
describe_images | Whether to describe the pictures found in the document. Requires model. Doc Chunker ignores it for audio and video. | false |
model | Model the inference gateway will use: the speech-to-text model for audio and video, the vision-language model for image description. A file needs at most one, so its type decides which. | |
chunk | Whether to also chunk the document and add chunks.json to the archive, which saves a round trip through POST /chunk. | false |
The archive holds document.json always, assets/ when the document has pictures, and chunks.json
when you pass chunk=true.
curl -X 'POST' \
'https://deepfellow-server-host/custom/doc_chunker/convert?chunk=true' \
-H 'Authorization: Bearer DEEPFELLOW-PROJECT-API-KEY' \
-F 'file=@report.pdf;type=application/pdf' \
-o report.zipimport io
import json
import zipfile
import requests
with open("report.pdf", "rb") as f:
response = requests.post(
"https://deepfellow-server-host/custom/doc_chunker/convert",
params={"chunk": "true"},
files={"file": ("report.pdf", f, "application/pdf")},
headers={"Authorization": "Bearer DEEPFELLOW-PROJECT-API-KEY"},
)
with zipfile.ZipFile(io.BytesIO(response.content)) as archive:
chunks = json.loads(archive.read("chunks.json"))
print(chunks)import * as fs from 'fs';
const buffer = await fs.promises.readFile('report.pdf');
const formData = new FormData();
formData.append('file', new Blob([buffer], { type: 'application/pdf' }), 'report.pdf');
const response = await fetch('https://deepfellow-server-host/custom/doc_chunker/convert?chunk=true', {
method: 'POST',
headers: {
Authorization: 'Bearer DEEPFELLOW-PROJECT-API-KEY'
},
body: formData
});
const archive = Buffer.from(await response.arrayBuffer());
await fs.promises.writeFile('report.zip', archive);document.json is the source of truth for everything downstream, so Doc Chunker produces it once and
never modifies it. Image descriptions are written while the document is being produced, so adding
them later means converting the file again.
Chunk a Document
POST /custom/doc_chunker/chunk accepts multipart/form-data with the document.json from a
POST /convert archive in the file field, and returns JSON. It converts nothing and contacts no
inference gateway, so it needs no model and leaves the document untouched. The assets aren't needed,
because chunk content comes from the document's own text and metadata.
curl -X 'POST' \
'https://deepfellow-server-host/custom/doc_chunker/chunk' \
-H 'Authorization: Bearer DEEPFELLOW-PROJECT-API-KEY' \
-F 'file=@document.json;type=application/json'The response holds the chunks and a total count.
{
"chunks": [
{
"text": "Quarterly revenue grew by 12 percent compared to the previous period.",
"meta": {
"headings": ["Financial Summary"],
"pages": [3],
"bboxes": [{ "page": 3, "l": 72.0, "t": 120.5, "r": 540.0, "b": 200.3 }],
"self_refs": ["#/texts/12"],
"labels": ["text"]
},
"start_index": 0,
"end_index": 69
}
],
"count": 1
}Every key in meta is conditional, and a chunk carries only the ones that apply to it.
| Key | Description |
|---|---|
headings | The headings the chunk sits under. |
pages | The pages the chunk's text comes from. |
bboxes | The bounding boxes of the chunk's text, as page, l, t, r, and b. |
self_refs | The references to the chunk's source items in document.json, for example #/texts/12. |
labels | The layout labels of the chunk's source items, for example text. |
start | For audio and video only, the start of the transcript window in seconds. |
end | For audio and video only, the end of the transcript window in seconds. |
start_index and end_index are character offsets into the concatenation of the chunk texts, in the
order Doc Chunker emitted them. They are not positions in the original file. Because the overlap
extends the earlier chunk, one chunk's end_index reaches past the next chunk's start_index, and
the difference is the length of the duplicated text, which is what you drop when you join chunks back
together. To locate a chunk within the original document, use self_refs, bboxes, or pages.
Errors
| Status | Meaning |
|---|---|
422 | Doc Chunker rejects the request: a missing file field, an unreadable file, a failed conversion, a missing or unknown model, or a malformed document.json. |
500 | An unhandled error inside Doc Chunker. |
502 | The inference gateway refused the request or was unreachable. |
504 | The conversion, one of its subprocesses such as LibreOffice or ffmpeg, or the inference gateway didn't finish in time. |
Doc Chunker enforces no upload size limit of its own, so it never answers 413. The limit on the
files you add to DeepFellow comes from DeepFellow Server.
Doc Chunker also exposes GET /custom/doc_chunker/health, which returns {"status": "ok"} when the
service is ready. It returns 503 when one of the conversion backends has exited or a conversion
has been running past its soft timeout. GET /custom/doc_chunker/version returns the version of the
running container.
We use cookies on our website. We use them to ensure proper functioning of the site and, if you agree, for purposes such as analytics, marketing, and targeting ads.