Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .coderabbit.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
reviews:
auto_review:
base_branches:
- .*
abort_on_close: false
6 changes: 3 additions & 3 deletions .github/workflows/smoke_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ jobs:
name: Create backup
run: |
mkdir ${PWD}/backup
docker compose run --rm -v ${PWD}/backup:/backup:rw --entrypoint "bash /app/openrag/scripts/entrypoint-backup.sh simplewiki-500" openrag-cpu
docker compose --env-file .env run --rm -v ${PWD}/backup:/backup:rw --entrypoint "bash /app/openrag/scripts/entrypoint-backup.sh simplewiki-500" openrag-cpu
wc -l backup/simplewiki-500.openrag

-
Expand All @@ -107,12 +107,12 @@ jobs:
-
name: Restore from modified backup
run: |
docker compose run --rm -v ${PWD}/backup:/backup:ro --entrypoint "bash /app/openrag/scripts/entrypoint-restore.sh simplewiki-500-2 /backup/tmp.openrag" openrag-cpu
docker compose --env-file .env run --rm -v ${PWD}/backup:/backup:ro --entrypoint "bash /app/openrag/scripts/entrypoint-restore.sh simplewiki-500-2 /backup/tmp.openrag" openrag-cpu

-
name: Create backup again
run: |
docker compose run --rm -v ${PWD}/backup:/backup:rw --entrypoint "bash /app/openrag/scripts/entrypoint-backup.sh simplewiki-500-2" openrag-cpu
docker compose --env-file .env run --rm -v ${PWD}/backup:/backup:rw --entrypoint "bash /app/openrag/scripts/entrypoint-backup.sh simplewiki-500-2" openrag-cpu
wc -l backup/simplewiki-500-2.openrag

-
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/smoke_test/.env
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ CONTEXTUAL_RETRIEVAL=false
# EMBEDDER
EMBEDDER_MODEL_NAME=ibm-granite/granite-embedding-small-english-r2
EMBEDDER_BASE_URL=http://vllm:8000/v1
# for ibm-granite/granite-embedding-small-english-r2. Adjust accordingly to your embedder model capabilities.
MAX_MODEL_LEN=8192
# EMBEDDER_API_KEY=EMPTY

# RERANKER
Expand Down
4 changes: 0 additions & 4 deletions .hydra_config/chunker/semantic_splitter.yaml

This file was deleted.

8 changes: 0 additions & 8 deletions .hydra_config/chunker/token_splitter.yaml

This file was deleted.

5 changes: 3 additions & 2 deletions .hydra_config/config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
defaults:
- _self_ # TODO: Silences the hydra version migration warning (PLEASE REVIEW FOR BREAKING CHANGES)
- chunker: ${oc.env:CHUNKER, recursive_splitter} # recursive_splitter, semantic_splitter, markdown_splitter
- chunker: ${oc.env:CHUNKER, recursive_splitter} # recursive_splitter
- retriever: ${oc.env:RETRIEVER_TYPE, single} # single # multiQuery # hyde
- rag: ChatBotRag

Expand Down Expand Up @@ -31,6 +31,7 @@ embedder:
model_name: ${oc.env:EMBEDDER_MODEL_NAME, jinaai/jina-embeddings-v3}
base_url: ${oc.env:EMBEDDER_BASE_URL, http://vllm:8000/v1}
api_key: ${oc.env:EMBEDDER_API_KEY, EMPTY}
max_model_len: ${oc.decode:${oc.env:MAX_MODEL_LEN, 8192}}

vectordb:
host: ${oc.env:VDB_HOST, milvus}
Expand All @@ -49,7 +50,7 @@ rdb:
reranker:
enable: ${oc.decode:${oc.env:RERANKER_ENABLED, true}}
model_name: ${oc.env:RERANKER_MODEL, Alibaba-NLP/gte-multilingual-reranker-base}
top_k: ${oc.decode:${oc.env:RERANKER_TOP_K, 5}} # Number of documents to return after reranking. upgrade to 8 for better results if your llm has a wider context window
top_k: ${oc.decode:${oc.env:RERANKER_TOP_K, 10}} # Number of documents to return after reranking. Upgrade for better results if your llm has a wider context window.
base_url: ${oc.env:RERANKER_BASE_URL, http://reranker:${oc.env:RERANKER_PORT, 7997}}

map_reduce:
Expand Down
8 changes: 3 additions & 5 deletions .hydra_config/rag/ChatBotRag.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
# Config for chatbot RAG
mode: ChatBotRag
chat_history_depth: 4
max_contextualized_query_len: 512

defaults:
- base
mode: ChatBotRag
5 changes: 3 additions & 2 deletions .hydra_config/rag/SimpleRag.yaml
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
mode: SimpleRag
chat_history_depth: 4
defaults:
- base
mode: SimpleRag
4 changes: 4 additions & 0 deletions .hydra_config/rag/base.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Config for chatbot RAG
mode: ''
chat_history_depth: 4
max_contextualized_query_len: 512
3 changes: 2 additions & 1 deletion .hydra_config/retriever/base.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
type: ''
top_k: ${oc.decode:${oc.env:RETRIEVER_TOP_K, 50}} # Number of documents to return before reranking
similarity_threshold: ${oc.decode:${oc.env:SIMILARITY_THRESHOLD, 0.6}} # Minimum similarity score for document retrieval
similarity_threshold: ${oc.decode:${oc.env:SIMILARITY_THRESHOLD, 0.6}} # Minimum similarity score for document retrieval
with_surrounding_chunks: ${oc.decode:${oc.env:WITH_SURROUNDING_CHUNKS, true}} # Whether to include surrounding chunks for each retrieved chunk
4 changes: 2 additions & 2 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ x-vllm: &vllm_template
--trust-remote-code
--task embed
--gpu_memory_utilization 0.3
--max-model-len ${MAX_MODEL_LEN:-8194}
--max-model-len ${MAX_MODEL_LEN:-8192}
# --max-num-seqs 1
# gpu_memory_utilization, max-num-seqs et max-model-len can be tuned depending on your GPU memory

Expand Down Expand Up @@ -143,7 +143,7 @@ services:
--model ${EMBEDDER_MODEL_NAME:-jinaai/jina-embeddings-v3}
--trust-remote-code
--dtype float32
--max-model-len ${MAX_MODEL_LEN:-8194}
--max-model-len ${MAX_MODEL_LEN:-8192}
# --max-num-batched-tokens 32768
# dtype is required for aarch64 (https://github.com/vllm-project/vllm/issues/11327) and improves speed on amd64.
# max-num-batched-tokens is required for aarch64 because chunked prefill isn't supported by V1 vllm backend
Expand Down
10 changes: 7 additions & 3 deletions docs/content/docs/documentation/API.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -218,27 +218,31 @@ curl -X POST http://localhost:8080/v1/chat/completions \
"content": "Your question here"
}
],
"temperature": 0.7,
"temperature": 0.1,
"stream": false
}'
```

You can also direclty use this endpoint with no RAG pipeline, i.e. to directly use the LLM.
For that, simply do not specify any model:
For that, instead of using the `openrag` prefix for the model, you can:
- Specify no model
- Specify an empty model
- Specify the openRAG configured model, e.g. `Mistral-Small-3.1-24B-Instruct-2503`.
Comment on lines 226 to +230

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix typo in user-facing documentation.

Line 226 contains a spelling error: "direclty" should be "directly". The expanded guidance for non-RAG usage is helpful and improves clarity.

Apply this diff to correct the typo:

-You can also direclty use this endpoint with no RAG pipeline, i.e. to directly use the LLM.
+You can also directly use this endpoint with no RAG pipeline, i.e. to directly use the LLM.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
You can also direclty use this endpoint with no RAG pipeline, i.e. to directly use the LLM.
For that, simply do not specify any model:
For that, instead of using the `openrag` prefix for the model, you can:
- Specify no model
- Specify an empty model
- Specify the openRAG configured model, e.g. `Mistral-Small-3.1-24B-Instruct-2503`.
You can also directly use this endpoint with no RAG pipeline, i.e. to directly use the LLM.
For that, instead of using the `openrag` prefix for the model, you can:
- Specify no model
- Specify an empty model
- Specify the openRAG configured model, e.g. `Mistral-Small-3.1-24B-Instruct-2503`.
🧰 Tools
🪛 LanguageTool

[grammar] ~226-~226: Ensure spelling is correct
Context: ... "stream": false }' ``` You can also direclty use this endpoint with no RAG pipeline,...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🤖 Prompt for AI Agents
In docs/content/docs/documentation/API.mdx around lines 226 to 230, fix the
spelling error "direclty" to "directly" in the sentence that explains using the
endpoint without a RAG pipeline; update that one word in-place so the sentence
reads "You can also directly use this endpoint..." and leave the rest of the
guidance unchanged.


**Request Body:**
```bash frame="none" title="Testing the openai OpenRAG chat completions endpoint with curl"
curl -X POST http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_AUTH_TOKEN" \
-d '{
"model": "",
"messages": [
{
"role": "user",
"content": "Your question here"
}
],
"temperature": 0.7,
"temperature": 0.1,
"stream": false
}'
```
Expand Down
14 changes: 8 additions & 6 deletions docs/content/docs/documentation/backup_restore.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ It's better to stop `openrag-cpu` (or `openrag`) service before starting backup.
:::

```bash
docker compose \
docker compose --env-file .env \
run \
--build \
--rm \
Expand All @@ -23,7 +23,7 @@ docker compose \
:::info
By default backup script creates plan text uncomressed file. To make things faster you can use multithread compressor the following way:
```bash
docker compose \
docker compose --env-file .env \
run \
--build \
--rm \
Expand All @@ -37,7 +37,8 @@ docker compose \
## Backup all partitions

```bash
docker compose run --build --rm \
docker compose --env-file .env \
run --build --rm \
-v ~/backup:/backup:rw \
--entrypoint "uv run /app/openrag/scripts/backup.py -o /backup/test.openrag" \
openrag
Expand All @@ -51,7 +52,7 @@ docker compose run --build --rm \
Start with dry run to ensure the backup file is correct:

```bash
docker compose \
docker compose --env-file .env \
run \
--build \
--rm \
Expand All @@ -62,7 +63,7 @@ docker compose \
Backup files are expected to be in `/my-backup-dir/`. If the dry run is successful, run the following script to insert the data :

```bash
docker compose \
docker compose --env-file .env \
run \
--build \
--rm \
Expand All @@ -74,7 +75,8 @@ docker compose \
## Restore all partitions

```bash
docker compose run --build --rm \
docker compose --env-file .env \
run --build --rm \
-v ~/backup:/backup:rw \
--entrypoint "uv run /app/openrag/scripts/restore.py /backup/test.openrag"\
openrag
Expand Down
12 changes: 5 additions & 7 deletions docs/content/docs/documentation/env_vars.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ As noted in [this PR](https://github.com/linagora/openrag/pull/134), the current

| Variable | Type | Default | Description |
|------------------------|------|----------------------|-------------|
| `CHUNKER` | `str` | recursive_splitter | Defines the chunking strategy: `recursive_splitter`, `semantic_splitter`, or `markdown_splitter`. |
| `CHUNKER` | `str` | recursive_splitter | Defines the chunking strategy: `recursive_splitter`. |
| `CONTEXTUAL_RETRIEVAL` | `bool` | true | Enables contextual retrieval to chunk context, a technique introduced by Anthropic to improve retrieval performance ([Contextual Retrieval](https://www.anthropic.com/news/contextual-retrieval)) |
| `CHUNK_SIZE` | `int` | 512 | Maximum size (in characters) of each chunk. |
| `CHUNK_OVERLAP_RATE` | `float`| 0.2 | Percentage of overlap between consecutive chunks. |
Expand All @@ -108,10 +108,6 @@ After files are converted to Markdown, only the **text content** is chunked.

* **`recursive_splitter`**: Uses hierarchical text structure (sections, paragraphs, sentences). Based on [RecursiveCharacterTextSplitter](https://docs.langchain.com/oss/python/integrations/splitters/index#text-structure-based), it preserves natural boundaries whenever possible while ensuring chunks never exceeding the `CHUNK_SIZE`.

* **`markdown_splitter`**: Splits text using Markdown headers, then subdivides sections that exceed `CHUNK_SIZE`.

* **`semantic_splitter`**: Uses embedding-based semantic similarity to create meaning-preserving chunks. Oversized chunks are chunked to be less than `CHUNK_SIZE`.

### Embedding
Our embedder is **OpenAI-compatible** and runs on a **VLLM** instance configured with the following variables:

Expand All @@ -120,6 +116,7 @@ Our embedder is **OpenAI-compatible** and runs on a **VLLM** instance configured
| `EMBEDDER_MODEL_NAME` | `str` | jinaai/jina-embeddings-v3 | HuggingFace Embedding model served by VLLM .i.e `Qwen/Qwen3-Embedding-0.6B` or `jinaai/jina-embeddings-v3`|
| `EMBEDDER_BASE_URL` | `str` | http://vllm:8000/v1 | Base URL of the embedder (OpenAI-style).|
| `EMBEDDER_API_KEY` | `str` | EMPTY | API key for authenticating embedder calls.|
| `MAX_MODEL_LEN` | `int` | 8192 | Maximum context length (in tokens) supported by the embedding model. If the chunk exceeds this limit, the embedder will truncate it.|

If you prefer to use an **external embedding service**, simply comment out the embedder service in the [docker-compose.yaml](https://github.com/linagora/openrag/blob/dev/docker-compose.yaml#L117-L153) and provide the variables above in your environment.

Expand Down Expand Up @@ -188,9 +185,10 @@ The retriever fetches relevant documents from the vector database based on query

| Variable | Type | Default | Description |
|----------|------|---------|-------------|
| `RETRIEVER_TYPE` | str | single | Retrieval strategy to use. Options: `single`, `multiQuery`, `hyde` |
| `RETRIEVER_TOP_K` | int | 50 | Number of documents to retrieve before reranking.|
| `SIMILARITY_THRESHOLD` | float | 0.6 | Minimum similarity score (0.0-1.0) for document retrieval. Documents below this threshold are filtered out |
| `RETRIEVER_TYPE` | str | single | Retrieval strategy to use. Options: `single`, `multiQuery`, `hyde` |
| `WITH_SURROUNDING_CHUNKS` | `bool` | true | When enabled, retrieves adjacent chunks (preceding and following) for each matched document to provide additional context.|

#### Retrieval Strategies

Expand All @@ -213,7 +211,7 @@ The current Infinity server interface is not OpenAI-compatible, which limits int
| `RERANKER_ENABLED` | `bool` | true | Enable or disable the reranking mechanism |
| `RERANKER_MODEL` | `str` | Alibaba-NLP/gte-multilingual-reranker-base | Model used for reranking documents.|
| `RERANKER_TOP_K` | `int` | 5 | Number of top documents to return after reranking. Increase to 8 for better results if your LLM has a wider context window |
| `RERANKER_BASE_URL` | `str` | http://reranker:7997 | Base URL of the reranker service |
| `RERANKER_BASE_URL` | `str` | `http://reranker:7997` | Base URL of the reranker service |
| `RERANKER_PORT` | `int` | 7997 | Port on which the reranker service listens |

## Extra
Expand Down
35 changes: 29 additions & 6 deletions openrag/components/files.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,35 @@
import asyncio
import re
import secrets
import time
from pathlib import Path
from typing import Dict, Optional

import aiofiles
from fastapi import UploadFile
import consts
import time
import secrets
import asyncio
from typing import Dict, Optional
from fastapi import UploadFile


def sanitize_filename(filename: str) -> str:
# Split filename into name and extension
path = Path(filename)
name = path.stem
ext = path.suffix

# Remove special characters (keep only word characters and hyphens temporarily)
name = re.sub(r"[^\w\-]", "_", name)

# Replace hyphens with underscores
name = name.replace("-", "_")

# Collapse multiple underscores
name = re.sub(r"_+", "_", name)

# Remove leading/trailing underscores
name = name.strip("_")

# Reconstruct filename
return name + ext
Comment on lines +13 to +32

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Handle edge case where sanitized name becomes empty.

If the filename consists only of special characters (e.g., ---.txt), the sanitized name becomes empty, resulting in just .txt. Consider adding a fallback:

     # Remove leading/trailing underscores
     name = name.strip("_")
 
+    # Fallback if name is empty after sanitization
+    if not name:
+        name = "file"
+
     # Reconstruct filename
     return name + ext
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def sanitize_filename(filename: str) -> str:
# Split filename into name and extension
path = Path(filename)
name = path.stem
ext = path.suffix
# Remove special characters (keep only word characters and hyphens temporarily)
name = re.sub(r"[^\w\-]", "_", name)
# Replace hyphens with underscores
name = name.replace("-", "_")
# Collapse multiple underscores
name = re.sub(r"_+", "_", name)
# Remove leading/trailing underscores
name = name.strip("_")
# Reconstruct filename
return name + ext
def sanitize_filename(filename: str) -> str:
# Split filename into name and extension
path = Path(filename)
name = path.stem
ext = path.suffix
# Remove special characters (keep only word characters and hyphens temporarily)
name = re.sub(r"[^\w\-]", "_", name)
# Replace hyphens with underscores
name = name.replace("-", "_")
# Collapse multiple underscores
name = re.sub(r"_+", "_", name)
# Remove leading/trailing underscores
name = name.strip("_")
# Fallback if name is empty after sanitization
if not name:
name = "file"
# Reconstruct filename
return name + ext
🤖 Prompt for AI Agents
In openrag/components/files.py around lines 13 to 32 the sanitized base name can
become empty (e.g., "---.txt") producing a filename like ".txt"; detect when
name is empty after stripping and replace it with a safe fallback (e.g., "file"
or "untitled") before reconstructing the filename, ensuring the extension is
preserved and the result does not start with a dot.



def make_unique_filename(filename: str) -> Path:
Expand Down Expand Up @@ -64,6 +88,5 @@ async def serialize_file(task_id: str, path: str, metadata: Optional[Dict] = {})
except Exception:
raise
else:

ray.cancel(future, recursive=True)
raise TimeoutError(f"Serialization task {task_id} timed out after seconds")
Loading
Loading