DeepFellow DOCS

Using Vector Stores

Vector Stores enable you to search for knowledge in files uploaded to DeepFellow. Search results consist of file chunks semantically similar to the query. Such search is a fundamental step in RAG systems (retrieval-augmented generation) that enable LLMs to ground their answers in knowledge from your uploaded files.

During DeepFellow Server Installation you have to configure the necessary components of working with a vector store system:

  • vector database provider - i.e. provider's specific vector database engine implementation (Qdrant or Milvus),
  • embedding model - model for converting text into vectors.

Goal

This tutorial shows how to create a vector store, upload a text file to it, and how to match your query with the relevant file content. It will demonstrate using two examples that DeepFellow's semantic search mechanism can differentiate between two files depending on the asked question.

Create a Vector Store

Let's create a sample vector store. To create a new vector make a POST /v1/vector_stores request:

curl -X 'POST' \
  "https://deepfellow-server-host/v1/vector_stores" \
  -H "Authorization: Bearer DEEPFELLOW-PROJECT-API-KEY" \
  -H 'Content-Type: application/json' \
  -d '{
  "chunking_strategy": {
    "type": "auto",
    "static": {
      "chunk_overlap_tokens": 100,
      "max_chunk_size_tokens": 200
    }
  },
  "name": "my_vector_store"
}'
import requests

response = requests.post(
    "https://deepfellow-server-host/v1/vector_stores",
    json={
        "chunking_strategy": {
            "type": "auto",
            "static": {
                "chunk_overlap_tokens": 100,
                "max_chunk_size_tokens": 200,
            },
        },
        "name": "my_vector_store",
    },
    headers={
        "Content-Type": "application/json",
        "Authorization": "Bearer DEEPFELLOW-PROJECT-API-KEY",
    },
)

print(response.json())
const response = await fetch('https://deepfellow-server-host/v1/vector_stores', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        Authorization: 'Bearer DEEPFELLOW-PROJECT-API-KEY'
    },
    body: JSON.stringify({
        chunking_strategy: {
            type: 'auto',
            static: {
                chunk_overlap_tokens: 100,
                max_chunk_size_tokens: 200
            }
        },
        name: 'my_vector_store'
    })
});

const data = await response.json();
console.log(data);

Response:

{
    "project_id": "68da445c5186deb8bca2bde9",
    "object": "vector_store",
    "name": "my_vector_store",
    "status": "completed",
    "file_counts": {
        "cancelled": 0,
        "completed": 0,
        "failed": 0,
        "in_progress": 0,
        "total": 0
    },
    "expires_after": null,
    "metadata": null,
    "chunking_strategy": {
        "type": "auto",
        "static": {
            "max_chunk_size_tokens": 200,
            "chunk_overlap_tokens": 100
        }
    },
    "id": "68ee6e626fef807dd40b2f80",
    "last_active_at": 1760449091,
    "created_at": 1760449091,
    "expires_at": 4914049091,
    "bytes": 0
}

The id field is the important bit of information used to identify concrete vector store in the next steps:

It's possible to add files to the vector store at creation time. To do so, you need to specify file_ids: string[].

"id": "68ee6e626fef807dd40b2f80"

During creation, each vector store is associated with the embedding model currently configured in the running DeepFellow Server upon its startup. So when you put your server down and change embedding model in config, all the vector stores created using previous embedding models become inactive until you change your settings back.

Upload Files

To upload a file use POST /v1/files endpoint. DeepFellow saves uploaded files to the path set by the DF_FILESTORAGE_TARGET_PATH environment variable (defaults to storage/).

Access rights

You can upload a file to a file storage using POST /v1/files endpoint. It doesn't automatically add this file to a vector store. To add a previously uploaded file to a vector store, use the following endpoint:

POST /v1/vector_stores/{vector_store_id}/files

Let's assume you have two files containing summary description for two companies.

  • secureo.txt
# Secureo Solutions

Secureo Solutions has established itself as a leading cybersecurity firm
specializing in enterprise-level threat detection and response systems. Founded
in 2018 by former government security analysts, the company offers a
comprehensive suite of services including penetration testing, security audits,
and 24/7 threat monitoring through their proprietary AI-powered platform,
ShieldWatch.
  • edulee.md
# Edulee Learning Platform

Edulee is an innovative online learning platform launched in 2019 that serves
over 2 million users worldwide with more than 15,000 courses in technology,
business, creative arts, and personal development. The platform partners with leading
companies like SoftABC, Searches, and ProDocs to ensure current content, while
offering flexible pricing tiers including a free option to make quality
education accessible to all.

Let's upload these files. For the first one:

curl -X 'POST' \
  "https://deepfellow-server-host/v1/files" \
  -H "Authorization: Bearer DEEPFELLOW-PROJECT-API-KEY" \
  -H 'Content-Type: multipart/form-data' \
  -F 'file=@secureo.txt;type=text/plain' \
  -F 'purpose=assistants'
import requests

url = "https://deepfellow-server-host/v1/files"
headers = {
    "Authorization": "Bearer DEEPFELLOW-PROJECT-API-KEY"
}
data = {
    "purpose": "assistants"
}

with open("secureo.txt", "rb") as f:
    files = {
        "file": ("secureo.txt", f, "text/plain")
    }
    response = requests.post(url, headers=headers, files=files, data=data)

print(response.json())
const url = 'https://deepfellow-server-host/v1/files';
const formData = new FormData();
const file = new File(['file content'], 'secureo.txt', { type: 'text/plain' });

formData.append('file', file);
formData.append('purpose', 'assistants');

const response = await fetch(url, {
    method: 'POST',
    headers: {
        Authorization: 'Bearer DEEPFELLOW-PROJECT-API-KEY'
    },
    body: formData
});

const data = await response.json();
console.log(data);

Response:

{
    "id": "68ee6e636fef807dd40b2f81",
    "project_id": "68da445c5186deb8bca2bde9",
    "object": "file",
    "bytes": 411,
    "filename": "secureo.txt",
    "purpose": "assistants",
    "created_at": 1760449091,
    "expires_at": 33296452691
}

The id field will be used for further requests.

Uploading the second file:

curl -X 'POST' \
  "https://deepfellow-server-host/v1/files" \
  -H "Authorization: Bearer DEEPFELLOW-PROJECT-API-KEY" \
  -H 'Content-Type: multipart/form-data' \
  -F 'file=@edulee.md;type=text/plain' \
  -F 'purpose=assistants'
import requests

url = "https://deepfellow-server-host/v1/files"
headers = {
    "Authorization": "Bearer DEEPFELLOW-PROJECT-API-KEY"
}
data = {
    "purpose": "assistants"
}

with open("secureo.txt", "rb") as f:
    files = {
        "file": ("edulee.md", f, "text/plain")
    }
    response = requests.post(url, headers=headers, files=files, data=data)


print(response.json())
const url = 'https://deepfellow-server-host/v1/files';
const formData = new FormData();
const file = new File(['file content'], 'edulee.md', { type: 'text/plain' });

formData.append('file', file);
formData.append('purpose', 'assistants');

const response = await fetch(url, {
    method: 'POST',
    headers: {
        Authorization: 'Bearer DEEPFELLOW-PROJECT-API-KEY"
    },
    body: formData
});

const data = await response.json();
console.log(data);

Response:

{
    "id": "68ee6e636fef807dd40b2f82",
    "project_id": "68da445c5186deb8bca2bde9",
    "object": "file",
    "bytes": 449,
    "filename": "edulee.md",
    "purpose": "assistants",
    "created_at": 1760449091,
    "expires_at": 33296452691
}

The id field will be used for further requests.

You cannot access files from the project you don't have access rights to. Each file can be only accessed within the project whose project API Key was used in the 'Authorization' header.

Add Uploaded File to Vector Store

Now add the uploaded files to the vector store in order to be able to search in them. Note that you don't need to add all the files you've uploaded – you have full control over which files are added to vector store.

For this task use POST /v1/vector_stores/{vector_store_id}/files endpoint. If Doc Chunker is enabled, DeepFellow saves the converted document, chunks, and extracted images to the path set by the DF_FILESTORAGE_META_PATH environment variable (defaults to storage/).

Add the first file:

curl -X 'POST' \
  "https://deepfellow-server-host/v1/vector_stores/68ee6e626fef807dd40b2f80/files" \
  -H "Authorization: Bearer DEEPFELLOW-PROJECT-API-KEY" \
  -H 'Content-Type: application/json' \
  -d '{ "file_id": "68ee6e636fef807dd40b2f81" }'
import requests

response = requests.post(
    "https://deepfellow-server-host/v1/vector_stores/68ee6e626fef807dd40b2f80/files",
    json={
        "file_id": "68ee6e636fef807dd40b2f81",
    },
    headers={
        "Content-Type": "application/json",
        "Authorization": "Bearer DEEPFELLOW-PROJECT-API-KEY",
    },
)

print(response.json())
const response = await fetch(
    'https://deepfellow-server-host/v1/vector_stores/68ee6e626fef807dd40b2f80/files',
    {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            Authorization: 'Bearer DEEPFELLOW-PROJECT-API-KEY'
        },
        body: JSON.stringify({
            file_id: '68ee6e636fef807dd40b2f81'
        })
    }
);

const data = await response.json();
console.log(data);

Response:

{
    "project_id": "68da445c5186deb8bca2bde9",
    "vector_store_id": "68ee6e626fef807dd40b2f80",
    "object": "vector_store.file",
    "last_error": null,
    "status": "in_progress",
    "usage_bytes": 411,
    "chunking_strategy": {
        "type": "auto",
        "static": {
            "max_chunk_size_tokens": 200,
            "chunk_overlap_tokens": 100
        }
    },
    "attributes": {},
    "id": "68ee6e636fef807dd40b2f81",
    "created_at": 1760449091
}

Add the second file:

curl -X 'POST' \
  "https://deepfellow-server-host/v1/vector_stores/68ee6e626fef807dd40b2f80/files" \
  -H "Authorization: Bearer DEEPFELLOW-PROJECT-API-KEY" \
  -H 'Content-Type: application/json' \
  -d '{ "file_id": "68ee6e636fef807dd40b2f82" }'
import requests

response = requests.post(
    "https://deepfellow-server-host/v1/vector_stores/68ee6e626fef807dd40b2f80/files",
    json={
        "file_id": "68ee6e636fef807dd40b2f82",
    },
    headers={
        "Content-Type": "application/json",
        "Authorization": "Bearer DEEPFELLOW-PROJECT-API-KEY",
    },
)

print(response.json())
const response = await fetch(
    'https://deepfellow-server-host/v1/vector_stores/68ee6e626fef807dd40b2f80/files',
    {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            Authorization: 'Bearer DEEPFELLOW-PROJECT-API-KEY'
        },
        body: JSON.stringify({
            file_id: '68ee6e636fef807dd40b2f82'
        })
    }
);

const data = await response.json();
console.log(data);

Response:

{
    "project_id": "68da445c5186deb8bca2bde9",
    "vector_store_id": "68ee6e626fef807dd40b2f80",
    "object": "vector_store.file",
    "last_error": null,
    "status": "in_progress",
    "usage_bytes": 449,
    "chunking_strategy": {
        "type": "auto",
        "static": {
            "max_chunk_size_tokens": 200,
            "chunk_overlap_tokens": 100
        }
    },
    "attributes": {},
    "id": "68ee6e636fef807dd40b2f82",
    "created_at": 1760449091
}

In the background, each of these files will be chunked into pieces. Then for each piece a vector will be generated to be semantically associated with the given chunk.

Add Multiple Files at Once with File Batches

Adding files one at a time works well for a handful of files, but it means one request per file. To attach many already-uploaded files to a vector store in a single trackable job, use POST /v1/vector_stores/{vector_store_id}/file_batches instead. A file batch reuses the same ingestion pipeline as POST /v1/vector_stores/{vector_store_id}/files, so each member file goes through the same chunking and embedding steps.

curl -X 'POST' \
  "https://deepfellow-server-host/v1/vector_stores/68ee6e626fef807dd40b2f80/file_batches" \
  -H "Authorization: Bearer DEEPFELLOW-PROJECT-API-KEY" \
  -H 'Content-Type: application/json' \
  -d '{
  "file_ids": ["68ee6e636fef807dd40b2f81", "68ee6e636fef807dd40b2f82"]
}'
import requests

response = requests.post(
    "https://deepfellow-server-host/v1/vector_stores/68ee6e626fef807dd40b2f80/file_batches",
    json={
        "file_ids": ["68ee6e636fef807dd40b2f81", "68ee6e636fef807dd40b2f82"],
    },
    headers={
        "Content-Type": "application/json",
        "Authorization": "Bearer DEEPFELLOW-PROJECT-API-KEY",
    },
)

print(response.json())
const response = await fetch(
    'https://deepfellow-server-host/v1/vector_stores/68ee6e626fef807dd40b2f80/file_batches',
    {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            Authorization: 'Bearer DEEPFELLOW-PROJECT-API-KEY'
        },
        body: JSON.stringify({
            file_ids: ['68ee6e636fef807dd40b2f81', '68ee6e636fef807dd40b2f82']
        })
    }
);

const data = await response.json();
console.log(data);

Response:

{
    "project_id": "68da445c5186deb8bca2bde9",
    "vector_store_id": "68ee6e626fef807dd40b2f80",
    "object": "vector_store.file_batch",
    "id": "68ee6e646fef807dd40b2f83",
    "created_at": 1760449092,
    "status": "in_progress",
    "file_counts": {
        "cancelled": 0,
        "completed": 0,
        "failed": 0,
        "in_progress": 2,
        "total": 2
    }
}

The id field identifies the batch for the endpoints below. status reflects the batch as a whole: it stays in_progress while at least one member file is still being processed, and moves to completed, cancelled, or failed once every member file reaches a terminal status, whichever fits.

To set per-file attributes or a chunking_strategy instead of applying the same values to every file, pass a files array of { file_id, attributes, chunking_strategy } objects in place of file_ids. file_ids and files are mutually exclusive, and a single batch holds at most 2,000 files.

Adding a file that's already a member of the vector store doesn't fail the batch. If the existing membership isn't failed or cancelled, the batch keeps it as-is instead of re-scheduling ingestion.

To check on a batch later, use GET /v1/vector_stores/{vector_store_id}/file_batches/{batch_id}. It returns the same shape as the creation response, with status and file_counts reflecting the current state.

To list the files that belong to a batch, use GET /v1/vector_stores/{vector_store_id}/file_batches/{batch_id}/files. It supports the same pagination and status filtering as listing a vector store's files, and returns entries in the same shape.

To stop a batch that's still processing, use POST /v1/vector_stores/{vector_store_id}/file_batches/{batch_id}/cancel. Member files still in_progress become cancelled; files that already finished keep their outcome. Calling cancel on a batch with no in_progress files has no effect.

Instead of polling GET /v1/vector_stores/{vector_store_id}/file_batches/{batch_id} for a batch to finish, configure a webhook to be notified as soon as it reaches a terminal status. See Webhooks.

Skip Chunking

Set chunking_strategy.type to skip to skip chunking entirely: each file becomes exactly one chunk containing its whole content, unchanged. Use this for files you already split into right-sized pieces and want the vector store to index as-is.

{
    "chunking_strategy": {
        "type": "skip"
    }
}

skip overrides the vector store's server-side chunking configuration for that request. The resulting file's chunking_mode is reported as skip in the response.

Describe Images before Chunking

Set image_processing_mode to description when you add a file to a vector store to have a vision model describe each image in the file before chunking. The description becomes part of the file's text, so image content becomes searchable together with the rest of the file. The default value, ignore, skips image descriptions and keeps the file's original text only.

POST /v1/vector_stores/{vector_store_id}/files and POST /v1/vector_stores's image_processing_mode accepts the same values when you add files through file_ids at creation time.

{
    "file_id": "68ee6e636fef807dd40b2f81",
    "image_processing_mode": "description"
}

DeepFellow caches a file's converted text after the first conversion. If a file was already converted without image descriptions, for example through Doc Chunker or an earlier upload, a later request with image_processing_mode set to description reuses that cached conversion as is, and DeepFellow logs a warning instead of describing the images. When retrieving a file's text directly with GET /v1/files/{file_id}/text, the same check runs, and the same warning is logged.

You cannot search in files from the project you don't have access rights to. Each file can be only accessed within the project whose project API Key was used in the 'Authorization' header.

Invent a query that can be related to the Edulee file and not to the Secureo. Then ask vector database using this query to show that chunks are matched only with the relevant file.

"query": "I want to learn about computers"

Vector search uses vector similarity measure (like a cosine similarity) to match a query with a relevant text from a vector store.

For this task use POST /v1/vector_stores/{vector_store_id}/search endpoint.

To search vector store for three best results type:

curl -X 'POST' \
  "https://deepfellow-server-host/v1/vector_stores/68ee6e626fef807dd40b2f80/search" \
  -H "Authorization: Bearer DEEPFELLOW-PROJECT-API-KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "query": "I want to learn about computers",
    "rewrite_query": true,
    "max_num_results": 3
  }'
import requests

response = requests.post(
    "https://deepfellow-server-host/v1/vector_stores/68ee6e626fef807dd40b2f80/search",
    json={
        "query": "I want to learn about computers",
        "max_num_results": 3,
        "rewrite_query": True
    },
    headers={
        "Content-Type": "application/json",
        "Authorization": "Bearer DEEPFELLOW-PROJECT-API-KEY",
    },
)

print(response.json())
const response = await fetch(
    'https://deepfellow-server-host/v1/vector_stores/68ee6e626fef807dd40b2f80/search',
    {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            Authorization: 'Bearer DEEPFELLOW-PROJECT-API-KEY'
        },
        body: JSON.stringify({
            query: 'I want to learn about computers',
            max_num_results: 3,
            "rewrite_query": true
        })
    }
);

const data = await response.json();
console.log(data);

Response:

{
    "object": "vector_store.search_results.page",
    "search_query": "I want to learn about computers",
    "data": [
        {
            "file_id": "68ee6e636fef807dd40b2f82",
            "filename": "edulee.md",
            "score": 0.5061247944831848,
            "attributes": {},
            "content": [
                {
                    "text": ", and ProDocs to ensure current content, while\noffering flexible pricing tiers including a free option to make quality\neducation accessible to all. \n",
                    "type": "text",
                    "offset": 0,
                    "metadata": {}
                }
            ]
        },
        {
            "file_id": "68ee6e636fef807dd40b2f82",
            "filename": "edulee.md",
            "score": 0.49376875162124634,
            "attributes": {},
            "content": [
                {
                    "text": "erves\nover 2 million users worldwide with more than 15,000 courses in technology,\nbusiness, creative arts, and personal development. The platform partners with leading\ncompanies like SoftABC, Searches",
                    "type": "text",
                    "offset": 0,
                    "metadata": {}
                }
            ]
        },
        {
            "file_id": "68ee6e636fef807dd40b2f82",
            "filename": "edulee.md",
            "score": 0.48902779817581177,
            "attributes": {},
            "content": [
                {
                    "text": " arts, and personal development. The platform partners with leading\ncompanies like SoftABC, Searches, and ProDocs to ensure current content, while\noffering flexible pricing tiers including a free opti",
                    "type": "text",
                    "offset": 0,
                    "metadata": {}
                }
            ]
        }
    ],
    "has_more": false,
    "next_page": null
}

Vector store returned chunks from the file containing info about education platform. It's expected as your query was related to learning.

Sometimes results are not satisfactory and the query needs refinement. You can adjust your search results using the following request body parameters:

{
    "filters": {
        "key": "string",
        "type": "eq", // different filter types are available [q, ne, gt, gte, lt, lte, in, nin]
        "value": "string"
    },
    "max_num_results": 10,
    "ranking_options": {
        "ranker": "auto"
        "score_threshold": 0.4
    }
}

filters can narrow down the scope of vectors to search. max_num_results limits the number of vectors returned. To return vectors which distance is above some threshold set score_threshold field.

Details on these parameters can be found in OpenAI API documentation.

How to Use Search Results

Search results can be used by LLMs to provide additional context in prompt to improve their answer in comparison to answering without context.

Prompt without context:

- Prompt: "What is Edulee?"
- LLM answer: "I don't have any specific information about Edulee."

Prompt with extra context:

- Prompt:"""

 <!-- This part is added programmatically in your app -->

Provided information in the context below, please answer the user prompt.
<context>
<entry>
File: edulee.md

[...] erves\nover 2 million users worldwide with more than 15,000 courses
in technology,\nbusiness, creative arts, and personal development.
The platform partners with leading\ncompanies like SoftABC, Searches [...]

</entry>
<entry>
File: edulee.md

[...] arts, and personal development. The platform partners with leading\ncompanies
like SoftABC, Searches, and ProDocs to ensure current content,
while\noffering flexible pricing tiers including a free opti [...]

</entry>
</context>
<!-- End of added context -->

What is Edulee?
"""

- LLM answer: "Edulee is a learning platform that offers 15,000 courses on various topics."

Final Notes

Vector stores open doors to building knowledge bases in your application. This is a major topic, programmatically based on RAG. Implementing knowledge bases securely is challenging but DeepFellow takes care of security and data safety for you.

For the model to retrieve from a vector store on its own, without you copying search results into the prompt yourself, see How to Create Your RAG.

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.