Skip to content

Documents

The Documents page holds the retrieval corpus. Uploaded files are parsed into chunks, each chunk is embedded, and a request that asks for retrieval has the best-matching chunks pasted into its system prompt before it reaches a provider.

Retrieval is core capability, not an entitlement — it is included at every licence tier.

The pipeline

Upload → pending → processing → ready
└──→ error
  1. The upload creates a document record in pending and enqueues the file.
  2. A worker parses it, splits it into chunks, embeds each chunk, and stores them.
  3. On success the document becomes ready and carries its chunk count.
  4. On failure it becomes error with the worker’s reason, shown verbatim on the row and in the drawer.

Only ready documents are retrievable. A document that is still processing is invisible to retrieval, and a request augmented from this corpus will answer as though it were never uploaded. The page states the count of non-ready documents above the table rather than leaving you to notice.

The worker is not optional. Without it, uploads sit in pending indefinitely.

The original file is not kept. Only the extracted chunks are stored, and there is no download endpoint.

File types

TypeDetected from
pdfapplication/pdf
markdowntext/markdown, or a .md filename
codeA source-file extension — .ts, .py, .go, .rs, .java, .sql, .vue and around thirty others
textEverything else

The maximum upload is 50 MB. PDFs are parsed; everything else is decoded as UTF-8. There is no format allow-list, so a binary file that is not a PDF will be accepted and decoded into nonsense — check the chunk preview in the drawer after uploading anything unusual.

You can override the detected type on upload.

Chunking

Prose (pdf, text, markdown)Code
Split onBlank lines, accumulating paragraphsLines, preferring function and class boundaries
Target size~2048 characters~26 lines, hard-split at double that
Overlap~200 characters carried into the next chunk
MetadataStart lineStart and end line

The overlap exists so a passage that straddles a boundary is still wholly present in one chunk.

Token counts on a chunk are an estimate — roughly four characters per token — not a real tokenisation.

Embeddings — read this before you deploy

Document embeddings are produced by Ollama, not by the local ONNX model the semantic cache uses.

VariableDefault
OLLAMA_URLhttp://localhost:11434
RAG_EMBEDDING_MODELnomic-embed-text

The same endpoint embeds the query at request time. So retrieval has a runtime dependency the rest of the gateway does not, and the two embedding stacks are not interchangeable — their vectors are different sizes and are not comparable.

If Ollama is unreachable, a document still completes. The failed embedding is caught, the chunk is stored with an empty vector, and the document reaches ready with a full chunk count. Retrieval skips chunks with no vector, so the document reads as retrievable and is reachable by nothing.

Nothing in the console distinguishes that state from a healthy one. Verify retrieval works after any change to the Ollama deployment — the Try a retrieval panel below is the fastest way to do it.

Trying a retrieval

The Try a retrieval panel runs the same code path a live request uses: a query, a chunk count (3 / 5 / 10 / 20) and a minimum match (0.50 loose, 0.70 default, 0.80 strict, 0.90 near-identical). Results show each hit’s score, its document and chunk index, and the chunk text.

Use it as the check that a corpus is actually live, not just uploaded.

Retrieval at request time

Retrieval runs only when a request asks for it — rag: true, or an options object { documentIds?, topK?, threshold? }. Requests that do not ask for it pay nothing.

  • The query is the last user message only. An empty one skips retrieval.
  • Defaults are top 5 chunks at a 0.7 minimum score.
  • A routing configuration with enableRag: false skips retrieval for requests addressed to it.
  • Every eligible chunk is scored, streamed in batches so memory stays bounded by the requested chunk count.
  • A 15-second deadline applies. Exceeding it fails the retrieval rather than returning what it had — a truncated scan returns confident answers that are simply not the best ones, which is worse than an honest failure.

Winning chunks are formatted and appended to the existing system message, or prepended as one if there is none:

[Document 1] (relevance: 87.3%)
<chunk content>
---
[Document 2] (relevance: 81.0%)

A retrieval failure never blocks the request. It is logged, and the request proceeds unaugmented. The caller sees a normal answer with no indication that retrieval did not happen — another reason to check the corpus deliberately rather than inferring health from successful completions.

The injected chunks are not recorded on the request log, so after the fact there is no way to see which chunks shaped a given completion.

Scoping

The document list is scoped: an organization sees its own documents and any uploaded with no organization set, which is how shared reference material is published to the whole deployment.

Retrieval currently resolves across the deployment. Do not use the corpus as a boundary for material that must not be visible between organizations; upload only what every user of the deployment may see.

Deleting

Deleting a document removes its chunks and their embeddings first, then the document itself. Anything a request was answering from it stops being retrievable immediately. Requests keep succeeding — they answer without it.

Endpoints

MethodPathPermission
GET/api/admin/documentsdocuments:read
POST/api/admin/documentsdocuments:create
GET/api/admin/documents/:iddocuments:read
GET/api/admin/documents/:id/chunksdocuments:read
POST/api/admin/documents/searchdocuments:read
DELETE/api/admin/documents/:iddocuments:delete

POST /documents is multipart/form-data with the file in a field named file, plus optional name and type.

POST /documents/search takes { query, topK?, threshold? } and returns the matching chunks with their scores. topK is passed through unclamped — ask for a sane number.

The list and chunk endpoints return a nested envelope unlike the rest of the admin API:

{ "data": { "items": [], "total": 0, "page": 1, "limit": 20, "totalPages": 0 } }

documents:* is granted to super-admin and deployment-admin only.

  • Cache — the other, unrelated embedding stack
  • Vector Stores — the caller-facing retrieval API
  • RoutingenableRag on a routing configuration