Configuration
The InfoConnect Hybrid Search Engine uses environment variables to control how it works. You can create a .env file in the project root. The system reads this file when it starts.
Getting Started with Configuration
Most users only need to change 3 to 5 settings to get started. The rest can use their default values.
Here is the minimum you need for your first setup:
# API key to client mapping (required for security)
API_KEY_CLIENTS={"my-secret-key-123":"my-client"}
# How fast users can call the API (optional)
RATE_LIMIT=30
# How much detail to show in logs (optional)
LOG_LEVEL=INFO
# Where your services run (default usually works)
REDIS_URL=redis://localhost:6379/0
QDRANT_URL=http://localhost:6333
Copy the example above into a file named .env in your project folder. Replace my-secret-key-123 with a strong random key. Then start the services with just dev.
The sections below explain each setting in detail. You can skip to the Example .env File for a complete reference.
Settings Most People Change
These are the settings you will likely want to adjust first.
API Keys and Access Control
| Variable | Default | Description |
|---|---|---|
API_KEY_CLIENTS | {} | JSON object mapping valid API keys to client ids for authentication |
RATE_LIMIT | 30 | Requests per minute per resolved client id for protected routes. See Rate Limiting guide for details. |
AUTH_FAILURE_RATE_LIMIT | 10 | Failed authentication attempts per minute per source address before a request can resolve to a client |
DEBUG | false | Enable debug mode (exposes /docs and /redoc) |
CORS_ORIGINS | ["http://localhost:3000"] | Allowed CORS origins |
The API_KEY_CLIENTS variable is required. Every request to the API must include one configured key in the X-API-Key header. This setting is parsed as a JSON object where each key maps to the client id that owns the request data. An empty object rejects every API key.
Set DEBUG=true only in development or trusted environments. It exposes /docs for interactive Swagger UI requests and /redoc for a cleaner read-only API reference. Keep DEBUG=false in production unless you intentionally want those documentation endpoints available.
You might give different keys to different teams or applications. This lets you rotate keys later without breaking everything at once.
Client isolation
Each API key maps to a client_id that owns the data it ingests. Every request — embed, search, update, delete, list — is automatically scoped to that client. Documents from different clients never mix, even when two clients use the same document_id. You do not pass client_id in your requests; the server resolves it from your API key.
If API_KEY_CLIENTS is empty or missing, the server rejects every request with 401 and logs a loud startup warning. This is intentional: the system fails closed rather than serving unscoped data.
For details on how client scoping affects search filters — including why client_id cannot be used as a filter key — see the Search Filters guide.
Example API Key Clients
# Development - simple keys are fine
API_KEY_CLIENTS={"test-key-123":"demo-client","another-key":"demo-client","super-secret-key":"demo-client"}
# Production - use strong random keys and stable client ids
API_KEY_CLIENTS={"prod-key-REPLACE_WITH_RANDOM_HEX":"prod-client"}
Logging Level
| Variable | Default | Description |
|---|---|---|
LOG_LEVEL | INFO | Logging level (DEBUG, INFO, WARNING, ERROR) |
Set this to DEBUG when troubleshooting. Use INFO for normal operation. Use WARNING or ERROR to reduce log noise in production.
Search and Document Settings
These settings control how documents are split and how search results are returned.
Document Chunking
| Variable | Default | Description |
|---|---|---|
CHUNK_SIZE | 512 | Approximate tokens per chunk |
CHUNK_OVERLAP | 50 | Token overlap between chunks |
When you upload a document, the InfoConnect Hybrid Search Engine splits it into smaller pieces called chunks. This helps the search work better. Larger chunks mean more context but slower processing. Smaller chunks mean more precise matches but less context.
Think of chunks like pages in a book. If your documents are short emails, use small chunks (256). If they are long reports, use larger chunks (1024). The default 512 works well for most documents.
Chunk Size Guidelines
| Use Case | Chunk Size | Overlap |
|---|---|---|
| Short documents (emails) | 256 | 25 |
| Standard documents | 512 | 50 |
| Long documents (reports) | 1024 | 100 |
Search Results
| Variable | Default | Description |
|---|---|---|
RETRIEVAL_TOP_K | 10 | Default search results count |
HYBRID_ALPHA | 0.5 | Default dense weight for hybrid search; supported range is 0.0 to 1.0 |
SEARCH_DENSE_SCORE_FLOOR | 0.70 | Minimum raw cosine score for a dense candidate |
SEARCH_SPARSE_SCORE_FLOOR | 1.0 | Minimum raw BM25 dot-product score for a sparse candidate |
SEARCH_TOTAL_COUNT_CAP | 1000 | Maximum score-gated count scan before reporting a lower bound; /search totals are independently bounded to the 200-result retrieval window |
RERANK_TOP_N | 5 | Number of results to rerank |
RERANK_TIMEOUT_SEC | 2.0 | Reranking timeout |
MAX_NODES_PER_TASK | 50 | Max nodes per search task |
RETRIEVAL_TOP_K controls how many results the search returns by default. HYBRID_ALPHA
sets the deployment-wide dense/sparse balance; an explicit /search alpha value overrides
it for that request. Start with HYBRID_ALPHA=0.5. Lower values favor sparse/lexical matches;
higher values favor dense/semantic matches.
The score floors gate raw dense and sparse candidates before hybrid fusion. The recommended
defaults are SEARCH_DENSE_SCORE_FLOOR=0.70 and SEARCH_SPARSE_SCORE_FLOOR=1.0; a candidate
is retained when either active stream clears its floor. Raising a floor can remove low-confidence
noise, but setting it too high over-filters genuine matches and can produce no results. Keep
gibberish and other queries with no confident match in the genuine no-results state. Recalibrate
the floors when the embedding model or corpus distribution changes. See the Search API reference for the full mechanism.
OCR and Classification Settings
These settings control how the InfoConnect Hybrid Search Engine reads images and categorizes documents.
Document Classification
| Variable | Default | Description |
|---|---|---|
CLASSIFICATION_ENABLED | true | Enable automatic document classification |
CLASSIFIER_BACKEND | modernbert-onnx | Classification backend selector |
When you upload a document, the InfoConnect Hybrid Search Engine can automatically figure out what category it belongs to. This helps organize your documents.
RAPIDOCR_MODEL_NAME is used for OCR during parsing.
ModernBERT ONNX is the only supported classification backend.
These model values reflect the repository's current .env.example / Docker mapping.
OCR for Images and PDFs
| Variable | Default | Description |
|---|---|---|
RAPIDOCR_OCR_ENABLED | true | Master switch to enable/disable OCR |
RAPIDOCR_MODEL_NAME | rapidocr-onnxruntime | OCR model for images and low-text PDF pages |
OCR_PAGE_TEXT_THRESHOLD | 80 | Min characters before OCR fallback |
If you upload an image or a scanned PDF, the InfoConnect Hybrid Search Engine uses RapidOCR to read the text. The threshold setting controls when it decides a PDF page needs OCR help.
Custom Categories
| Variable | Default | Description |
|---|---|---|
ALLOWED_CATEGORIES | ["financial reporting","risk management","corporate governance","regulatory compliance","business operations","market analysis","legal","human capital","technology","sustainability","other"] | JSON array of category labels scored by ModernBERT |
CATEGORY_DESCRIPTIONS | KYC/Credit/Collateral/Ledger descriptions | Optional JSON object mapping category labels to richer scoring text. Missing descriptions fall back to the category label. |
Current defaults come from app/config.py and use the ModernBERT financial / corporate taxonomy:
["financial reporting","risk management","corporate governance","regulatory compliance","business operations","market analysis","legal","human capital","technology","sustainability","other"]
These settings are also parsed as JSON arrays. If you customize them, keep the JSON syntax:
ALLOWED_CATEGORIES=["financial reporting","technology","other"]
You can customize these to match your business, but the classifier still expects JSON arrays rather than plain delimited strings.
CATEGORY_DESCRIPTIONS is optional. You do not need to provide a description for every item in ALLOWED_CATEGORIES. If a category has no matching description, the InfoConnect Hybrid Search Engine falls back to the label text itself. For example, Ledger without a description is scored as Ledger.
Descriptions are strongly recommended for short, ambiguous, acronym-based, or client-specific labels. Labels such as KYC, Credit, Collateral, and Ledger often need richer text because real documents may say Kad Pengenalan, bankruptcy search, pledged asset, or journal entry instead of the exact label.
ALLOWED_CATEGORIES=["KYC","Credit","Collateral","Ledger"]
CATEGORY_DESCRIPTIONS={"KYC":"know your customer, identity verification, customer due diligence, IC, passport, Kad Pengenalan, Warganegara, onboarding, or beneficial ownership documents","Credit":"credit reports, bankruptcy searches, insolvency checks, lending, repayment history, or credit risk documents"}
You can describe only the categories that need extra context. The API still returns the configured category label, such as KYC; descriptions are used only for scoring and strong lexical evidence matching.
ModernBERT ONNX Settings
| Variable | Default | Description |
|---|---|---|
MODERNBERT_ONNX_MODEL_REPO | onnx-community/ModernBERT-base-nli-ONNX | Hugging Face repo for the ONNX classifier |
MODERNBERT_ONNX_MODEL_FILE | onnx/model_quantized.onnx | ONNX model file loaded from the repo |
MODERNBERT_ONNX_CATEGORY_TEMPLATE | The topic of this document is {label}. | Hypothesis template for category scoring |
MODERNBERT_ONNX_CATEGORY_THRESHOLD | 0.45 | Minimum score required to keep a category |
MODERNBERT_ONNX_LONG_DOC_WINDOW | 1800 | Sliding window size (tokens) for documents exceeding model context |
MODERNBERT_ONNX_LONG_DOC_OVERLAP | 256 | Token overlap between consecutive windows |
MODERNBERT_ONNX_TOP_K_CHUNKS | 2 | Max additional chunks to score after the first (long-doc optimization) |
MODERNBERT_ONNX_NLI_MAX_TOKENS | 2048 | Maximum token length for NLI input sequences (premise + hypothesis). Lower values reduce inference time at the cost of truncating long document text |
CLASSIFICATION_SELECT_TOP_K | 3 | Number of representative chunks selected via centroid + MMR for post-embedding classification. Classification scores these chunks instead of the full document text. |
MODERNBERT_ONNX_INTRA_OP_THREADS | 1 | ONNX Runtime intra-op thread count |
MODERNBERT_ONNX_INTER_OP_THREADS | 1 | ONNX Runtime inter-op thread count |
MODERNBERT_PREFILTER_ENABLED | false | Enable lexical prefilter to reduce hypothesis space before ONNX inference |
MODERNBERT_PREFILTER_TOP_K | 3 | Number of top categories to keep after lexical prefilter |
Available Model Repos (MODERNBERT_ONNX_MODEL_REPO)
The classifier requires a 3-class NLI model outputting entailment (index 0), neutral (index 1), contradiction (index 2). Only repos with this exact label mapping are compatible.
| Repo | Size | NLI Classes | Status |
|---|---|---|---|
onnx-community/ModernBERT-base-nli-ONNX | Base (768d, 22 layers) | 3 (entailment / neutral / contradiction) | ✅ Compatible |
onnx-community/ModernBERT-large-zeroshot-v2.0-ONNX | Large (1024d, 28 layers) | 2 (entailment / not_entailment) | ❌ Incompatible — crashes at runtime (logits.shape[1] < 3) |
Do not use 2-class models like ModernBERT-large-zeroshot-v2.0-ONNX. The pipeline expects exactly 3 NLI output logits and will raise a shape validation error on startup or during inference.
Available Model Files (MODERNBERT_ONNX_MODEL_FILE)
These files exist in the upstream repository, but the current commercial-use allowlist accepts only
onnx/model_quantized.onnx. Selecting another file through an environment override fails startup.
Adding another artifact requires a code and policy change with license evidence and review.
| File | Precision | Size | Notes |
|---|---|---|---|
onnx/model.onnx | fp32 | ~599 MB | Highest accuracy; not currently allowlisted. |
onnx/model_fp16.onnx | fp16 | ~300 MB | Near-lossless accuracy. Good balance for most deployments. |
onnx/model_quantized.onnx | int8 | ~151 MB | Approved default; good accuracy/size tradeoff. |
onnx/model_int8.onnx | int8 | ~151 MB | Same quantization as model_quantized.onnx. |
onnx/model_uint8.onnx | uint8 | ~151 MB | Alternative 8-bit quantization. |
onnx/model_q4.onnx | 4-bit | ~225 MB | Aggressive quantization. Lower accuracy, smaller download. |
onnx/model_q4f16.onnx | 4-bit/fp16 | ~140 MB | Smallest file. Noticeable accuracy loss. |
onnx/model_bnb4.onnx | BNB 4-bit | ~218 MB | BitsAndBytes-style 4-bit quantization. |
Use onnx/model_quantized.onnx for current deployments. Other upstream artifacts are reference
options only until explicitly reviewed and added to the commercial-use allowlist.
Classification runs asynchronously after embedding and typically takes 2-4 seconds per document. The classifier reads chunk embeddings from Qdrant, selects representative chunks via centroid + MMR, and scores only those chunks with ModernBERT. See Architecture: Post-Embedding Classification for the trigger, retry, and reconciliation contract.
Thread counts default to 1 across all environments. Increase MODERNBERT_ONNX_INTRA_OP_THREADS for CPU-bound preprocessing workers if your deployment has spare cores.
Named Entity Recognition
| Variable | Default | Description |
|---|---|---|
SPACY_NER_ENABLED | true | Enable spaCy NER extraction |
NER_ENTITY_KEYS | See below | Entity types to extract and store |
The InfoConnect Hybrid Search Engine can find and extract important information from your documents. This includes names, dates, amounts of money, and more.
Default Entity Keys
The following entity types are extracted and stored:
persons— People namesorganizations— Companies, agenciesdates— Date and time referenceslocations— Places, countries, citiesmonetary_amounts— Currency values (regex-enhanced)account_numbers— Account identifiers (regex)transaction_refs— Transaction references (regex)account_types— Account types (regex)
Worker and Performance Settings
These settings control background processing and resource usage.
Celery Worker Settings
| Variable | Default | Description |
|---|---|---|
CELERY_WORKER_PREFETCH_MULTIPLIER | 1 | Tasks prefetched per worker |
CELERY_WORKER_REVISION | unknown | Deployment marker used by the startup worker-isolation guard |
CELERY_TASK_ACKS_LATE | true | Acknowledge after task completion |
CELERY_WORKER_MAX_MEMORY_PER_CHILD | 1572864 (1.5GB) | Memory limit per worker (KB) |
CELERY_WORKER_MAX_TASKS_PER_CHILD | 100 | Tasks before worker restart |
CELERY_WORKER_MAX_TASKS_PER_CHILD_EMBEDDING | 250 | Embedding-worker task recycle target from config.py |
CELERY_WORKER_MAX_TASKS_PER_CHILD_PREPROCESSING | 500 | Preprocessing-worker task recycle target from config.py |
CELERY_TASK_MAX_RETRIES | 3 | Max retry attempts for failed tasks |
CELERY_RETRY_BACKOFF_BASE | 60 | Base seconds for exponential backoff |
CELERY_RESULT_EXPIRES | 86400 (24h) | Result retention time (seconds) |
CELERY_EMBED_SOFT_TIME_LIMIT_SEC | 900 | Soft time limit for embedding tasks |
CELERY_EMBED_TIME_LIMIT_SEC | 1200 | Hard time limit for embedding tasks |
CELERY_TASK_REJECT_ON_WORKER_LOST | true | Requeue task if a worker exits unexpectedly |
Workers are background processes that handle document processing. When you upload a file, the API accepts it immediately. Then workers do the heavy lifting: parsing, chunking, embedding, and storing. This happens in the background so the API stays fast.
Workers automatically restart after processing a certain number of tasks or using too much memory. This prevents memory leaks from slowing things down.
just dev and just prod-up set CELERY_WORKER_REVISION to the current 12-character Git revision and include that value in every worker node name. If you start the API or workers manually, use the same valid revision marker for all processes and name workers as preprocessing-<revision>@<host> or embedding-<number>-<revision>@<host>.
app/config.py keeps the general CELERY_WORKER_MAX_TASKS_PER_CHILD default at 100, but docker-compose.yml overrides the live worker values to 250 for embedding workers and 500 for the preprocessing worker.
app/config.py defaults CELERY_WORKER_MAX_MEMORY_PER_CHILD to 1572864 KB (about 1.5 GB). The preprocessing worker in docker-compose.yml can override this via PROD_PREPROCESSING_MAX_MEMORY_PER_CHILD_KB (default 4194304 KB / 4 GB) to match its larger container memory limit.
Docker / Runtime-only Overrides
| Variable | Default | Description |
|---|---|---|
PROD_PREPROCESSING_MEMORY | 6G | Docker Compose memory limit for worker-preprocessing-1 |
This variable is only used by docker-compose.yml at runtime. It does not exist in app/config.py, but it controls the container memory limit for the preprocessing worker in production-style Docker runs.
Redis Configuration
| Variable | Default | Description |
|---|---|---|
REDIS_URL | redis://localhost:6379/0 | Redis connection for caching |
CELERY_BROKER_URL | redis://localhost:6379/1 | Celery task queue |
CELERY_RESULT_BACKEND | redis://localhost:6379/2 | Celery results store |
Redis is used for two things: caching and task queuing. The URLs point to different databases on the same Redis server.
Redis Database Usage
| DB | Purpose |
|---|---|
| 0 | Application caching |
| 1 | Celery task broker |
| 2 | Celery results backend |
Qdrant Configuration
| Variable | Default | Description |
|---|---|---|
QDRANT_URL | http://localhost:6333 | Qdrant server URL |
COLLECTION_NAME | documents | Vector collection name |
VECTOR_SIZE | 384 | Embedding dimensions (must match model) |
Qdrant is the vector database that stores your document embeddings. This is what makes semantic search possible.
Embedding Models
| Variable | Default | Description |
|---|---|---|
DENSE_MODEL_NAME | BAAI/bge-small-en-v1.5 | Dense embedding model |
SPARSE_MODEL_NAME | Qdrant/bm25 | Sparse/BM25 model |
RERANK_MODEL_NAME | Xenova/ms-marco-MiniLM-L-6-v2 | FastEmbed cross-encoder reranker |
EMBEDDING_BATCH_SIZE | 32 | Embeddings per batch |
FASTEMBED_CACHE_DIR | ./models/fastembed | Model cache location |
Embedding models turn text into numbers that capture meaning. The InfoConnect Hybrid Search Engine uses two types: dense (for semantic meaning) and sparse (for keyword matching). Together they give better search results.
The listed model identifiers are the reviewed commercial-use defaults. Arbitrary environment overrides are rejected; changing a model requires updating the runtime and compliance allowlists.
Upstream Dense Model References
| Model | Dimensions | Speed | Quality | Commercial allowlist |
|---|---|---|---|---|
BAAI/bge-small-en-v1.5 | 384 | Fast | Good | Approved default |
BAAI/bge-base-en-v1.5 | 768 | Medium | Better | Not reviewed |
BAAI/bge-large-en-v1.5 | 1024 | Slow | Best | Not reviewed |
sentence-transformers/all-MiniLM-L6-v2 | 384 | Fast | Good | Not reviewed |
Larger models may improve quality but use more memory and run slower. They are reference options, not runtime configuration choices, until their licenses and artifacts are reviewed and allowlisted.
File Upload Security
| Variable | Default | Description |
|---|---|---|
MAX_FILE_SIZE_MB | 200 | Maximum upload file size in MB |
ALLOWED_EXTENSIONS | .pdf,.docx,.txt,.md,.text,.pptx,.ppt,.xlsx,.xls,.png,.jpg,.jpeg,.tif,.tiff | Permitted file extensions |
ALLOWED_MIME_TYPES | See example below | Permitted upload MIME types mapped to extensions |
UPLOAD_DIR | ./models/uploads (/tmp/ic-uploads in Docker examples) | Local staging directory for files before Celery processing |
OFFICE_LEGACY_CONVERSION_ENABLED | false | Enable LibreOffice conversion of legacy .ppt/.xls to OOXML before parsing |
LIBREOFFICE_BINARY | soffice | LibreOffice executable used for legacy Office conversion |
OFFICE_LEGACY_CONVERSION_TIMEOUT_SEC | 120 | Timeout for a single legacy Office conversion |
OFFICE_MAX_UNCOMPRESSED_BYTES | 1000000000 | Max decompressed size of an OOXML (.docx/.pptx/.xlsx) upload (zip-bomb guard) |
OFFICE_MAX_COMPRESSION_RATIO | 200 | Max uncompressed:compressed ratio for an OOXML upload (zip-bomb guard) |
These settings protect your server from oversized uploads and unexpected file types. A file must pass the extension check, declared MIME mapping check when a known MIME type is provided, and sniffed-content check before it is enqueued.
UPLOAD_DIR is especially important in Docker deployments. The API writes the uploaded file there first, then the preprocessing worker reads it from the same path. If you change UPLOAD_DIR, make sure the API and worker containers mount the same shared volume at that location.
Production Runtime Threading
| Variable | Default | Description |
|---|---|---|
PROD_EMBEDDING_OMP_THREADS | 1 | Auto-tuned OpenMP/BLAS thread count for embedding worker containers |
OMP_NUM_THREADS | From PROD_EMBEDDING_OMP_THREADS in Docker Compose | OpenMP thread cap inside embedding workers |
OPENBLAS_NUM_THREADS | From PROD_EMBEDDING_OMP_THREADS in Docker Compose | OpenBLAS thread cap inside embedding workers |
MKL_NUM_THREADS | From PROD_EMBEDDING_OMP_THREADS in Docker Compose | MKL thread cap inside embedding workers |
scripts/generate_prod_runtime.py writes PROD_EMBEDDING_OMP_THREADS to .runtime/prod.env; Docker Compose passes it through to the lower-level numerical libraries. Keep this at 1 unless benchmark evidence shows spare CPU headroom.
Model Cache Paths
| Variable | Default | Description |
|---|---|---|
FASTEMBED_CACHE_DIR | ./models/fastembed | FastEmbed model cache |
HF_HOME | /app/.cache/huggingface | Hugging Face cache |
HF_TOKEN | (unset) | Optional Hugging Face token for gated or rate-limited model downloads |
SPACY_MODEL_NAME | en_core_web_sm | spaCy NER model |
These control where approved models are stored. You usually do not need to change them. Set HF_TOKEN
only when downloads of the approved Hugging Face repository are rate-limited; switching repositories or
spaCy models requires a reviewed code and compliance-policy change.
Example .env File
Here is a complete example .env file with all common settings, organized by ergonomics — the settings you change most often appear first:
# =============================================================================
# 1. Identity & Access
# =============================================================================
API_KEY_CLIENTS={"dev-key-123":"dev-client","dev-key-456":"dev-client"}
RATE_LIMIT=60
AUTH_FAILURE_RATE_LIMIT=10
# =============================================================================
# 2. Core Behavior
# =============================================================================
DEBUG=true
CORS_ORIGINS=["http://localhost:3000","http://localhost:8080"]
LOG_LEVEL=INFO
RETRIEVAL_TOP_K=10
HYBRID_ALPHA=0.5
SEARCH_DENSE_SCORE_FLOOR=0.70
SEARCH_SPARSE_SCORE_FLOOR=1.0
SEARCH_TOTAL_COUNT_CAP=1000
RERANK_TOP_N=5
MAX_FILE_SIZE_MB=200
ALLOWED_EXTENSIONS=[".pdf",".docx",".txt",".md",".text",".pptx",".ppt",".xlsx",".xls",".png",".jpg",".jpeg",".tif",".tiff"]
ALLOWED_MIME_TYPES={"application/pdf":".pdf","application/vnd.openxmlformats-officedocument.wordprocessingml.document":".docx","application/vnd.openxmlformats-officedocument.presentationml.presentation":".pptx","application/vnd.ms-powerpoint":".ppt","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":".xlsx","application/vnd.ms-excel":".xls","text/plain":".txt","text/markdown":".md","image/png":".png","image/jpeg":".jpg"}
# =============================================================================
# 3. Domain Taxonomy (Client-Customizable)
# =============================================================================
ALLOWED_CATEGORIES=["financial reporting","risk management","corporate governance","regulatory compliance","business operations","market analysis","legal","human capital","technology","sustainability","other"]
NER_ENTITY_KEYS=["persons","organizations","dates","locations","monetary_amounts","account_numbers","transaction_refs","account_types"]
# =============================================================================
# 4. Pipeline Tuning
# =============================================================================
CHUNK_SIZE=512
CHUNK_OVERLAP=50
EMBEDDING_BATCH_SIZE=32
MAX_NODES_PER_TASK=50
RAPIDOCR_OCR_ENABLED=true
OCR_PAGE_TEXT_THRESHOLD=80
# =============================================================================
# 5. Classification & Entity Extraction
# =============================================================================
CLASSIFICATION_ENABLED=true
SPACY_NER_ENABLED=true
CLASSIFIER_BACKEND=modernbert-onnx
SPACY_MODEL_NAME=en_core_web_sm
CLASSIFICATION_SELECT_TOP_K=3
MODERNBERT_ONNX_MODEL_REPO=onnx-community/ModernBERT-base-nli-ONNX
MODERNBERT_ONNX_MODEL_FILE=onnx/model_quantized.onnx
MODERNBERT_ONNX_CATEGORY_TEMPLATE="The topic of this document is {label}."
MODERNBERT_ONNX_CATEGORY_THRESHOLD=0.45
MODERNBERT_ONNX_LONG_DOC_WINDOW=1800
MODERNBERT_ONNX_LONG_DOC_OVERLAP=256
MODERNBERT_ONNX_TOP_K_CHUNKS=2
MODERNBERT_ONNX_NLI_MAX_TOKENS=2048
MODERNBERT_ONNX_INTRA_OP_THREADS=1
MODERNBERT_ONNX_INTER_OP_THREADS=1
MODERNBERT_PREFILTER_ENABLED=false
MODERNBERT_PREFILTER_TOP_K=3
# =============================================================================
# 6. AI / ML Models
# =============================================================================
DENSE_MODEL_NAME=BAAI/bge-small-en-v1.5
SPARSE_MODEL_NAME=Qdrant/bm25
RERANK_MODEL_NAME=Xenova/ms-marco-MiniLM-L-6-v2
RERANK_TIMEOUT_SEC=2.0
VECTOR_SIZE=384
FASTEMBED_CACHE_DIR=./models/fastembed
HF_HOME=./models/huggingface
HF_TOKEN=
# =============================================================================
# 7. Infrastructure
# =============================================================================
TZ=Asia/Kuala_Lumpur
REDIS_URL=redis://localhost:6379/0
CELERY_BROKER_URL=redis://localhost:6379/1
CELERY_RESULT_BACKEND=redis://localhost:6379/2
QDRANT_URL=http://localhost:6333
COLLECTION_NAME=documents
# =============================================================================
# Worker Performance (optional)
# =============================================================================
CELERY_WORKER_MAX_TASKS_PER_CHILD=100
CELERY_WORKER_MAX_TASKS_PER_CHILD_EMBEDDING=250
CELERY_WORKER_MAX_TASKS_PER_CHILD_PREPROCESSING=500
CELERY_WORKER_MAX_MEMORY_PER_CHILD=4194304
CELERY_EMBED_SOFT_TIME_LIMIT_SEC=900
CELERY_EMBED_TIME_LIMIT_SEC=1200
CELERY_TASK_REJECT_ON_WORKER_LOST=true
Configuration Best Practices
Recommended Settings for Production
DEBUG=false
LOG_LEVEL=INFO
RATE_LIMIT=30
API_KEY_CLIENTS={"REPLACE_WITH_A_STRONG_RANDOM_KEY":"prod-client"}
CELERY_WORKER_MAX_TASKS_PER_CHILD=100
CELERY_WORKER_MAX_MEMORY_PER_CHILD=1572864 # 1.5GB
Docker Compose
Environment variables are automatically loaded from .env when using:
just prod-up
just prod-up also writes .runtime/prod.env with computed
startup values such as API worker count and worker scale targets so operators can inspect the chosen runtime plan.
Or set in docker-compose.yml:
services:
api:
environment:
- API_KEY_CLIENTS=${API_KEY_CLIENTS}
- LOG_LEVEL=INFO
After making changes to .env, restart the services to apply them:
just stop
just dev
What's Next
Now that you understand the configuration options, you can:
- Follow the Quick Start Guide to get your first document uploaded
- Read the Architecture Overview to understand how everything fits together
- Review the Justfile Commands for common development tasks
If something is not working as expected, check the logs with LOG_LEVEL=DEBUG and review the troubleshooting section in the installation guide.