DeepFellow DOCS

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.

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. If INFRA_API_URL points at this Infra, leave INFRA_API_KEY empty: Infra fills in its own API key when it creates the container. For any other gateway that requires a key, set INFRA_API_KEY yourself.

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 Doc Chunker default of 7200 seconds. DeepFellow Server runs each conversion as a Doc Chunker job: it submits the file, reads the job status every few seconds, and downloads the result when the job ends. No gateway request lasts for the whole conversion, so the proxy timeout only has to cover one upload or one result download. A Doc Chunker image without the job API converts each file in one request, and the gateway cuts off any conversion that runs longer than the proxy timeout. For such an image, keep the proxy timeout at or above doc_chunker_convert_timeout_seconds. See Proxy Timeout and Conversion Timeouts.

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.

DeepFellow Server routes every file 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 before you add files to a vector store.

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_prefix: Endpoint prefix the server uses to reach Doc Chunker. It must match the prefix you set when you installed the model. Default doc_chunker.
  • doc_chunker_picture_description_model: Vision-language model the server sends as the vision_model parameter when it asks Doc Chunker to describe pictures. Default gemma4:e4b.
  • doc_chunker_stt_model: Speech-to-text model the server sends as the stt_model parameter with every conversion. Doc Chunker uses it only for audio and video. Default Systran/faster-whisper-large-v3.
  • doc_chunker_convert_timeout_seconds: Seconds the server waits for one conversion to finish, including the time the file waits in Doc Chunker's queue. It applies to every conversion: vector store ingestion, Document Summarization, Knowledge Graph ingestion, and file text retrieval. When the time runs out, the request fails with 504. Default 7200.
  • doc_chunker_poll_interval_seconds: Seconds between two reads of a conversion job's status. Default 5.
  • doc_chunker_request_timeout_seconds: Seconds one file upload or one result download takes at most. It does not limit the conversion itself. Default 300.

All six are dynamic settings, so a change applies immediately and never needs a restart:

deepfellow server config set doc_chunker_prefix=doc_chunker

The Doc Chunker section of the Server Web Panel holds all six: doc_chunker_prefix as Prefix, doc_chunker_picture_description_model as Picture Description Model, doc_chunker_stt_model as Speech-to-Text Model, doc_chunker_convert_timeout_seconds as Conversion Timeout (seconds), doc_chunker_poll_interval_seconds as Conversion Poll Interval (seconds), and doc_chunker_request_timeout_seconds as Request Timeout (seconds).

The matching environment variables, DF_DOC_CHUNKER_PREFIX, DF_DOC_CHUNKER_PICTURE_DESCRIPTION_MODEL, DF_DOC_CHUNKER_STT_MODEL, DF_DOC_CHUNKER_CONVERT_TIMEOUT_SECONDS, DF_DOC_CHUNKER_POLL_INTERVAL_SECONDS, and DF_DOC_CHUNKER_REQUEST_TIMEOUT_SECONDS, 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.

Conversion Timeouts

A conversion of a large file, such as a long recording or a PDF with many pictures, takes up to about an hour. Five timeouts apply to it, and each one must be at least as long as the one it wraps. Otherwise an outer layer cuts the conversion short or runs it a second time:

TimeoutWhere it is setDefault
DOCUMENT_TIMEOUTDeepFellow Infra install form of the doc_chunker model. Caps the conversion stage inside Doc Chunker.3600
doc_chunker_convert_timeout_secondsDeepFellow Server dynamic setting. Caps a whole conversion: the wait in Doc Chunker's queue, the conversion, and the image descriptions.7200
proxy_timeout_seconds of the doc_chunker installDeepFellow Infra install form. Caps one gateway request. It matters here only for a Doc Chunker image without the job API, where one request carries the whole conversion.7200
nightshift_jobs_timeout_secondsDeepFellow Server dynamic setting. Caps a Nightshift job step: the conversion plus the model calls after it.10800
RabbitMQ consumer_timeoutRabbitMQ broker. Caps how long a message stays unacknowledged before the broker redelivers it.4h in the bundled compose.yaml, 30 min on a stock broker

To convert files that take longer than the defaults allow, raise them together and keep them in this order. For more on the last two, see Nightshift Background Jobs.

If DeepFellow Server or a Nightshift worker stops during a conversion, the retry resumes the same Doc Chunker job instead of converting the file again. If Doc Chunker's job queue is full, or Doc Chunker no longer holds the job, the conversion fails with 503, and Nightshift retries it.

A doc_chunker install that left proxy_timeout_seconds empty picks up the 7200-second default on the next DeepFellow Infra restart. DOCUMENT_TIMEOUT is saved with the install, so an install made with the earlier 600 default keeps it. To raise it, change it in Edit Settings on the model's card in the Infra Web Panel, or reinstall the model.

Update Doc Chunker

Updating DeepFellow Infra does not update an installed Doc Chunker. To move it to a newer image, click Update in the ⋮ menu of the doc_chunker card in the Infra Web Panel, or run deepfellow infra model update custom doc_chunker in the CLI. deepfellow infra model versions custom doc_chunker lists the available versions. See Updates.

The update recreates the container, so register your tokenizers again afterward. See Data Storage.

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

VariableDescriptionDefault
MAX_CONCURRENTNumber of worker slots. Each slot runs one conversion or one chunking at a time, whether it comes from /convert, /chunk, or a conversion job. See Worker Slots.2
DOCUMENT_TIMEOUTMaximum time in seconds for a single conversion. Keep it below the server's doc_chunker_convert_timeout_seconds. See Conversion Timeouts.3600
LIBREOFFICE_TIMEOUTMaximum 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.

Worker Slots

/convert, /chunk, and conversion jobs share the MAX_CONCURRENT worker slots. Each slot has a role, which decides what it takes first when it frees up.

SlotTakes FirstThen
Async-priority, set by ASYNC_PRIORITY_WORKERSA conversion job/convert and /chunk
Sync-only, set by SYNC_ONLY_WORKERS/convert and /chunkNothing else
Every other slot/convert and /chunkA conversion job

Waiting work of one kind runs in arrival order.

VariableDescriptionDefault
ASYNC_PRIORITY_WORKERSNumber of async-priority slots.1, or 0 when MAX_CONCURRENT is 1
SYNC_ONLY_WORKERSNumber of sync-only slots.0

With the default of 2 slots, one slot prefers conversion jobs and the other prefers /convert and /chunk. While jobs wait in the queue, /convert and /chunk effectively have one slot.

Doc Chunker refuses to start unless ASYNC_PRIORITY_WORKERS plus SYNC_ONLY_WORKERS is at most MAX_CONCURRENT, and each of the two is below MAX_CONCURRENT. Then at least one slot takes /convert and /chunk first, and at least one slot takes conversion jobs.

When no /convert or /chunk request waits, every slot that isn't sync-only takes a conversion job. A /convert request that arrives then waits until one of those jobs finishes, which takes up to an hour. DeepFellow Server sends every conversion through /convert, so vector store ingestion waits too. To keep one slot free for /convert and /chunk at all times, set SYNC_ONLY_WORKERS to 1. Conversion jobs then get one slot fewer.

The Infra install form exposes neither ASYNC_PRIORITY_WORKERS nor SYNC_ONLY_WORKERS, so an install from the Infra Web Panel runs with their defaults.

Chunk Size

A chunk holds at most as many tokens as the chunking request asks for in max_tokens. A request that omits the parameter gets the largest chunk recorded for the tokenizer that measures it, which is 512 tokens for the built-in default. See Chunking Tokenizers.

Doc Chunker fills 70 percent of that budget 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 30 percent ceiling of the budget.

Doc Chunker overlaps only consecutive chunks of running text, so a table never bleeds into its neighbors.

Chunk boundaries are measured with the tokenizer of the embedding model the chunks are destined for, so the token counts match the ones that model counts. Name that model in the embedding_model parameter of a chunking request, and register its tokenizer first. See Chunking Tokenizers.

The chunking_strategy parameter of the vector store endpoints does not set chunk boundaries. Doc Chunker takes them from embedding_model and max_tokens. The only value that changes the result is the skip strategy, which keeps the whole converted file as a single chunk.

Data Storage

Doc Chunker keeps everything it stores under DATA_DIR, in two subdirectories it creates there: tokenizers/ holds the registered tokenizers, and jobs/ holds the conversion jobs with their results. Chunking never writes to DATA_DIR.

Doc Chunker refuses to start when it can't write to both subdirectories. It doesn't check that DATA_DIR is a mounted volume. Without one, Doc Chunker writes to the container's own file system, and every registered tokenizer and every job result is lost when the container is recreated, for example on a reinstall or an upgrade.

The Infra install form of the doc_chunker model mounts no volume at DATA_DIR. An install from the Infra Web Panel therefore keeps its tokenizers and job results only until its container is recreated. Register your tokenizers again after a reinstall or an upgrade.

VariableDescriptionDefault
DATA_DIRDirectory holding tokenizers/ and jobs/. Mount a writable volume here./var/lib/doc-chunker
MAX_TOKENIZERSNumber of registered tokenizers Doc Chunker holds, in memory and on the volume together.10
TOKENIZER_MAX_ARCHIVE_BYTESLargest accepted tokenizer upload, in bytes. The default is 50 MB.52428800
TOKENIZER_MAX_UNPACKED_BYTESLargest accepted unpacked size, in bytes. The default is 200 MB.209715200

Registering a tokenizer past MAX_TOKENIZERS deletes the least recently used one from memory and from the volume. That model then answers like one that was never registered, and registering its tokenizer again brings it back. Recency lives in memory, so right after a restart, before anything has been used, Doc Chunker deletes the tokenizer that was registered longest ago.

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.

VariableDescriptionDefault
INFRA_API_URLGateway root, for example http://ollama:11434. If empty, an install through Infra uses http://infra:8086.
INFRA_API_KEYAPI key for the gateway, if it requires one. If empty and INFRA_API_URL is this Infra (http://infra:8086 or DF_INFRA_URL), Infra fills in its own API key.

Infra sends its API key only to its own address, never to an external gateway. After you change the Infra API key, Doc Chunker keeps the previous key until you restart Infra or reinstall Doc Chunker. A duplicated Doc Chunker model doesn't get the key filled in. Set its INFRA_API_KEY yourself.

An install through Infra fills in INFRA_API_URL with http://infra:8086 when you leave it empty. In a Doc Chunker container run outside Infra, an empty INFRA_API_URL makes both features unavailable, 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 vision_model. Both are required together: a request that asks for descriptions without vision_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.

VariableDescriptionDefault
PICTURE_DESCRIPTION_PROMPTPrompt that controls how the model describes each image.Describe this image in detail.
PICTURE_DESCRIPTION_API_MAX_TIMEMaximum wait time in seconds for a single gateway call.300
PICTURE_DESCRIPTION_CONCURRENCYNumber 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 stt_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.

VariableDescriptionDefault
AUDIO_STT_MAX_TIMEMaximum wait time in seconds for a single speech-to-text call.600
AUDIO_SILENCE_THRESHOLDMinimum 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 uses 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 conversion, set image_processing_mode to description in the request body. The default value, ignore, skips image descriptions. The setting applies only when the conversion 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.

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.

Chunking Tokenizers

Every embedding model counts tokens with its own tokenizer, so a chunk that fits one model overflows another. Doc Chunker measures each chunking request with the tokenizer of the embedding model the chunks are destined for. Two query parameters select it, and both POST /custom/doc_chunker/chunk and POST /custom/doc_chunker/convert?chunk=true accept them.

ParameterDescriptionDefault
embedding_modelEmbedding model the chunks are destined for. Its registered tokenizer measures the boundaries.The built-in default
max_tokensLargest number of tokens a chunk will hold.The largest chunk recorded for the selected tokenizer

One tokenizer ships inside the image, for the mxbai-embed-large model, with a maximum of 512 tokens. A request that names no embedding_model uses it, and a request that names mxbai-embed-large resolves to the same tokenizer. It never counts against MAX_TOKENIZERS, and Doc Chunker never downloads it, so a container with no outbound network and an empty volume still chunks.

To chunk for any other embedding model, register that model's tokenizer first.

Doc Chunker answers 400 when embedding_model names a model with no registered tokenizer, and when max_tokens is above the maximum recorded for the selected tokenizer. Neither case falls back to the default, and neither reduces max_tokens to the maximum, because chunks measured with the wrong tokenizer look exactly like correct ones.

Register a Tokenizer

POST /custom/doc_chunker/tokenizers accepts multipart/form-data with a ZIP archive in the file field, and registers the tokenizer it holds for one embedding model.

ParameterDescription
modelEmbedding model this tokenizer belongs to. Required.
max_tokensLargest number of tokens this model embeds at once. Required. It becomes both the default and the ceiling for max_tokens on chunking requests.

Spell model exactly as your inference gateway spells it, tags and namespaces included, for example mxbai-embed-large:latest or mixedbread-ai/mxbai-embed-large-v1. Doc Chunker takes the name as a key rather than a path and returns it verbatim, so a-model:v1 and a-model:v2 are two separate registrations. A name that is blank or longer than 200 characters is rejected with 400.

curl -X 'POST' \
  'https://deepfellow-server-host/custom/doc_chunker/tokenizers?model=nomic-embed-text&max_tokens=8192' \
  -H 'Authorization: Bearer DEEPFELLOW_PROJECT_API_KEY' \
  -F 'file=@nomic-embed-text-tokenizer.zip;type=application/zip'

The response repeats what is now registered.

{
    "model": "nomic-embed-text",
    "max_tokens": 8192
}

Registering a model that already has a tokenizer replaces it. Doc Chunker loads the archive before it stores anything, so a rejected upload leaves the previous tokenizer in place and usable.

What the archive holds

Pack a Hugging Face tokenizer directory with the files at the root of the archive, not inside a folder. It is what AutoTokenizer.from_pretrained(model).save_pretrained(directory) writes:

FileDescription
tokenizer.jsonRequired.
tokenizer_config.jsonThe tokenizer class and its special tokens. Required in practice.
special_tokens_map.jsonIncluded when the model has one.

Everything else in a model repository is the model rather than the tokenizer, so config.json, model.safetensors, onnx/, and 1_Pooling/ stay out of the archive. Doc Chunker accepts only a tokenizer backed by a tokenizer.json, and rejects a sentencepiece-only tokenizer or one that ships its own Python class with 400. It loads tokenizers with no remote code and no network access, so an upload never executes code and never pulls files from outside the archive.

Read a Registered Tokenizer

GET /custom/doc_chunker/tokenizers/{model} returns the model name and its recorded max_tokens, in the shape POST /custom/doc_chunker/tokenizers returns. It answers 404 when nothing is registered for that model. The name is taken as a path, so a namespaced name needs no escaping.

curl -X 'GET' \
  'https://deepfellow-server-host/custom/doc_chunker/tokenizers/nomic-embed-text' \
  -H 'Authorization: Bearer DEEPFELLOW_PROJECT_API_KEY'

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.

ParameterDescriptionDefault
describe_imagesWhether to describe the pictures found in the document. Requires vision_model. Doc Chunker ignores it for audio and video.false
vision_modelVision-language model the inference gateway will use to describe pictures. Required with describe_images=true. Doc Chunker ignores it otherwise.
stt_modelSpeech-to-text model the inference gateway will use to transcribe audio and video. Required for audio and video. Doc Chunker ignores it for other files.
chunkWhether to also chunk the document and add chunks.json to the archive, which saves a round trip through POST /chunk.false
embedding_modelEmbedding model the chunks are destined for, as described in Chunking Tokenizers. Doc Chunker ignores it without chunk=true.The built-in default
max_tokensLargest number of tokens a chunk will hold. Doc Chunker ignores it without chunk=true.The largest chunk recorded for the selected tokenizer

The archive holds document.json always, assets/ when the document has pictures, and chunks.json when you pass chunk=true.

Doc Chunker checks embedding_model and max_tokens before it starts converting, so an unregistered model or an impossible token limit costs one rejected request rather than a conversion whose result never reaches you.

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.zip
import 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.

Convert a File in the Background

POST /custom/doc_chunker/convert answers only once the conversion is done, which takes up to an hour for a large file. A conversion job lets you submit the file, disconnect, and come back for the result. DeepFellow Server itself always converts through POST /convert, so conversion jobs are for direct calls only.

EndpointDescription
POST /custom/doc_chunker/convert/jobsSubmits a conversion and answers 202 with the job's status. It takes the same file field and query parameters as POST /convert.
GET /custom/doc_chunker/convert/jobs/{job_id}Returns the job's status.
GET /custom/doc_chunker/convert/jobs/{job_id}/resultReturns the ZIP archive POST /convert would have returned for the same request, named after the uploaded file. Answers 409 until the job has succeeded.
DELETE /custom/doc_chunker/convert/jobs/{job_id}Removes a queued job, which then never runs, or a finished job with its result, and answers 204. Answers 409 with the detail job_running for a running job.

The status and result endpoints answer 404 for an id that Doc Chunker doesn't hold, either because it never existed or because the job has expired.

Conversion jobs need the project's custom_endpoints set to all. DeepFellow Server matches each path exactly, and every path after the submission holds the job's id, so no fixed list of paths covers them. See Call Doc Chunker Directly.

Submit the file:

curl -X 'POST' \
  'https://deepfellow-server-host/custom/doc_chunker/convert/jobs?chunk=true' \
  -H 'Authorization: Bearer DEEPFELLOW_PROJECT_API_KEY' \
  -F 'file=@report.pdf;type=application/pdf'

The response, and every status request after it, returns the job's status:

{
    "id": "3f9a0c1e5b7d4e8a9c217d4e5f6a8b90",
    "status": "failed",
    "filename": "report.pdf",
    "created_at": "2026-09-29T12:00:00Z",
    "started_at": "2026-09-29T12:00:04Z",
    "finished_at": "2026-09-29T12:41:10Z",
    "error": { "status_code": 422, "detail": "..." }
}

status is queued, running, succeeded, or failed. A failed job's error holds the status code and the detail POST /convert would have answered with for the same failure. Treat the id as an opaque string.

Once status is succeeded, download the result and delete the job:

curl -X 'GET' \
  'https://deepfellow-server-host/custom/doc_chunker/convert/jobs/JOB_ID/result' \
  -H 'Authorization: Bearer DEEPFELLOW_PROJECT_API_KEY' \
  -o report.zip

curl -X 'DELETE' \
  'https://deepfellow-server-host/custom/doc_chunker/convert/jobs/JOB_ID' \
  -H 'Authorization: Bearer DEEPFELLOW_PROJECT_API_KEY'

Job Lifecycle

  • Retention: Doc Chunker removes a finished job and its result JOB_RESULT_TTL seconds after the job finished. Delete a job once you have downloaded its result, so it doesn't occupy the volume until then.
  • Restarts: Finished jobs survive a restart of Doc Chunker. A job that was queued or running when Doc Chunker stopped becomes failed, with the detail interrupted by restart. Doc Chunker doesn't run it again, so submit the file again if you still need the result.
  • Full queue: When MAX_QUEUED_JOBS jobs already wait for a slot, a submission answers 503 with the detail queue_full_retry_later and no Retry-After header, because Doc Chunker has no estimate of when a slot frees up.
  • Full volume: When the volume has no room for the uploaded file, a submission answers 507.
VariableDescriptionDefault
JOB_RESULT_TTLSeconds a finished job's status and result stay available, counted from when the job finished. The default is 24 hours.86400
MAX_QUEUED_JOBSNumber of jobs that wait for a slot before Doc Chunker refuses new submissions.100

Conversion jobs run on the same worker slots as /convert and /chunk, as described in Worker Slots. The Infra install form doesn't expose JOB_RESULT_TTL or MAX_QUEUED_JOBS, so an install from the Infra Web Panel runs with their defaults. Job results live under DATA_DIR, as described in Data Storage.

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 conversion 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 embedding_model and max_tokens parameters decide the chunk boundaries. This call measures with the built-in default tokenizer, at its own maximum. To measure for another embedding model, add both parameters, as described in Chunking Tokenizers.

curl -X 'POST' \
  'https://deepfellow-server-host/custom/doc_chunker/chunk?embedding_model=nomic-embed-text&max_tokens=2048' \
  -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.

KeyDescription
headingsThe headings the chunk sits under.
pagesThe pages the chunk's text comes from.
bboxesThe bounding boxes of the chunk's text, as page, l, t, r, and b.
self_refsThe references to the chunk's source items in document.json, for example #/texts/12.
labelsThe layout labels of the chunk's source items, for example text.
startFor audio and video only, the start of the transcript window in seconds.
endFor 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

StatusMeaning
400The request names or carries something Doc Chunker will not use: an embedding model with no registered tokenizer, a max_tokens above the maximum recorded for that tokenizer, an unusable model name, or an upload that is not a loadable tokenizer or is over the size limits.
404GET /tokenizers/{model} finds nothing registered for that model, or /convert/jobs/{job_id} finds no job held under that id.
409GET /convert/jobs/{job_id}/result asks for the result of a job that hasn't succeeded, or DELETE /convert/jobs/{job_id} targets a running job (job_running).
422Doc Chunker rejects the request: a missing file field, an unreadable file, a failed conversion, a missing or unknown vision_model or stt_model, or a malformed document.json.
500An unhandled error inside Doc Chunker, for example a registered tokenizer whose stored metadata is no longer readable.
502The inference gateway refused the request or was unreachable.
503Nothing is wrong with the request, so retry it later. No worker slot was free and host memory stayed too tight to admit the conversion, a conversion backend failed on every attempt, or POST /convert/jobs found the queue full (queue_full_retry_later).
504The conversion, one of its subprocesses such as LibreOffice or ffmpeg, or the inference gateway didn't finish in time.
507POST /convert/jobs found no room on the volume for the uploaded file.

Doc Chunker enforces no size limit of its own on the files it converts, so it never answers 413. The limit on the files you add to DeepFellow comes from DeepFellow Server. A tokenizer upload is the one exception: Doc Chunker rejects an archive over TOKENIZER_MAX_ARCHIVE_BYTES with 400, from the Content-Length header, before it reads the archive into memory.

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.