📚 Docs / Configuration Reference — Complete Environment Variables & Settings

Configuration Reference — Complete Environment Variables & Settings

Comprehensive configuration guide for Lawyer Assistant. Every tunable parameter, environment variable, and configuration file documented in one place.

1. Configuration hierarchy

Settings are read in order of precedence (highest wins):

1. Environment variables (PLR_*)     ← highest priority
2. Per-project settings (workspace/history.json)
3. File-based config (api_config.json)
4. Code defaults (config.py)         ← lowest priority

Example:

python# Default in config.py
embedding_batch_size = 32

# Override via environment variable
export PLR_EMBEDDING_BATCH_SIZE=16
# → batch size is now 16

2. Model configuration

Embedding model (BGE-M3)

Env VarDefaultTypeMeaning
PLR_EMBEDDING_MODEL_DIRmodels/bge-m3PathLocal model directory
PLR_EMBEDDING_DIM1024IntEmbedding output dimension (BGE-M3 fixed)
PLR_EMBEDDING_MAX_LENGTH512IntMax tokens per text (512 is optimal for 480-token chunks)
PLR_EMBEDDING_BATCH_SIZE32IntCAP batch size (GPU manager may tune down from free VRAM)
PLR_EMBEDDING_USE_FP16TrueBoolFP16 faster on GPU, auto-disabled on CPU
PLR_EMBEDDING_NORMALIZETrueBoolL2-normalize embeddings (required for cosine similarity)

Example:

bash# Lower batch size for 4GB GPU
export PLR_EMBEDDING_BATCH_SIZE=16

# Force CPU embedding
export PLR_DEVICE=cpu
export PLR_EMBEDDING_USE_FP16=False

Reranker model (BGE-Reranker-v2-M3)

Env VarDefaultTypeMeaning
PLR_RERANKER_MODEL_DIRmodels/bge-reranker-v2-m3PathLocal model directory
PLR_RERANKER_MAX_LENGTH512IntMax tokens per query+doc pair
PLR_RERANKER_BATCH_SIZE32IntCAP batch size (GPU manager may tune down)
PLR_RERANKER_USE_FP16TrueBoolFP16 faster on GPU, auto-disabled on CPU

Example:

bash# Smaller batch for low-VRAM GPUs
export PLR_RERANKER_BATCH_SIZE=16

LLM (Ollama)

Env VarDefaultTypeMeaning
PLR_LLM_NUM_GPU0IntGPU layers (0 = CPU-only, -1 = use GPU fully)
PLR_LLM_KEEP_ALIVE5mStrHow long LLM stays loaded after request
PLR_LLM_NUM_THREAD0IntCPU threads (0 = auto)
PLR_LLM_VRAM_ESTIMATE_MB5000IntFallback VRAM estimate for 8B-class LLM
PLR_OLLAMA_BASE_URLhttp://localhost:11434URLOllama HTTP API endpoint

Why PLR_LLM_NUM_GPU=0 by default:

Example:

bash# Use LLM on GPU (faster generation, slower search)
export PLR_LLM_NUM_GPU=-1

# Unload LLM immediately after each call (free RAM)
export PLR_LLM_KEEP_ALIVE=0

Device selection

Env VarDefaultTypeMeaning
PLR_DEVICEautoStrauto \

Auto resolution:

  1. Try CUDA (NVIDIA GPU)
  2. Try MPS (Apple Silicon)
  3. Fall back to CPU

Example:

bash# Force CPU (no GPU needed)
export PLR_DEVICE=cpu

3. GPU manager configuration

See GPU_MANAGEMENT.md for GPU manager design.

Env VarDefaultTypeMeaning
PLR_GPU_MANAGER_ENABLED1BoolMaster switch (0 = disabled, transparent no-op)
PLR_GPU_HEADROOM_MB256IntVRAM kept free for driver/display/other apps
PLR_GPU_RESIDENCY_MODEadaptiveStreager \
PLR_GPU_KEEP_RESIDENT_FREE_MB1024Int(Adaptive) Min free VRAM to keep hot model resident
PLR_GPU_HOT_MIN_USES2Int(Adaptive) Uses to become "hot"
PLR_GPU_HOT_WINDOW_SECONDS120Float(Adaptive) Sliding window for hotness tracking
PLR_GPU_MAX_RESIDENT2Int(Adaptive) Max models resident simultaneously
PLR_GPU_AUTO_BATCH1BoolAuto-tune batch sizes from free VRAM
PLR_GPU_BATCH_SAFETY_MB256IntVRAM reserve when auto-tuning batch
PLR_GPU_EVICT_OLLAMA1BoolAllow ollama stop under VRAM pressure
PLR_GPU_DEMOTE_AFTER_USEBoolLegacy: 1→eager, 0→persistent (still honored)

Residency modes:

Example:

bash# Aggressive GPU freeing (tiny GPU)
export PLR_GPU_RESIDENCY_MODE=eager
export PLR_GPU_MAX_RESIDENT=1

# Persistent (capable GPU)
export PLR_GPU_RESIDENCY_MODE=persistent
export PLR_GPU_MAX_RESIDENT=3

4. Chunking configuration

Config FieldDefaultTypeMeaning
chunk_size480IntTarget tokens per chunk
overlap0IntOverlap tokens between adjacent chunks
respect_sectionsTrueBoolNever split across section boundaries
min_chunk_size32IntDiscard chunks smaller than this
unittokenStrtoken \

No env var overrides — chunking config is code-only (edit config.py).

Example (code override):

pythonfrom legal_retrieval.config import get_config, Config, ChunkingConfig
from dataclasses import replace

cfg = get_config()
new_chunking = replace(cfg.chunking, chunk_size=600, overlap=50)
new_cfg = replace(cfg, chunking=new_chunking)

5. Storage configuration

ChromaDB

Env VarDefaultTypeMeaning
PLR_CHROMA_DIRdata/chroma_dbPathChromaDB persistent directory (workspace overrides this)

Other settings (code-only):

Workspace override:

Paths

All path defaults are in config.pyPathConfig:

PathDefaultEnv Var
data_dirdata/
raw_dirdata/raw/
processed_dirdata/processed/
models_dirmodels/
logs_dirlogs/

Workspace overrides:


6. Search & retrieval configuration

Config FieldDefaultTypeMeaning
top_k_retrieval50IntInitial retrieval from ChromaDB + BM25
top_k_final5IntFinal result count after reranking
min_score_threshold0.0FloatDrop chunks below this reranker score
rerankTrueBoolEnable reranking
search_modehybridStrdense \
rrf_k60IntRRF constant (higher = less weight to rank)
hybrid_merge_strategycombine_and_dedupStrcombine_and_dedup \

No env var overrides — search config is:

  1. Code defaults (config.py)
  2. Runtime pipeline config (POST /api/pipeline/config)
  3. Per-request overrides (ChatRequest.skip_rerank, top_k)

Example (runtime config):

bash# Apply pipeline layout → reconfigures search
curl -X POST http://localhost:8765/api/pipeline/config \
  -H "Content-Type: application/json" \
  -d @pipeline-layout.json

Example (per-request override):

bash# Single query with top_k=10, skip rerank
curl -X POST http://localhost:8765/api/chat \
  -H "Content-Type: application/json" \
  -d '{"query":"What is rescission?","top_k":10,"skip_rerank":true}'

7. Ingestion configuration

Config FieldDefaultTypeMeaning
do_ocrTrueBoolOCR for scanned PDFs
do_table_structureTrueBoolExtract table structure
ocr_lang["en"]ListOCR languages
generate_page_imagesTrueBoolGenerate page images (improves OCR)
image_dpi150IntImage DPI for OCR fallback
max_pagesNoneIntMax pages per document (None = unlimited)

No env var overrides — ingestion config is code-only.

Example (disable OCR for faster ingestion):

pythonfrom legal_retrieval.config import get_config, Config, IngestionConfig
from dataclasses import replace

cfg = get_config()
new_ingestion = replace(cfg.ingestion, do_ocr=False, generate_page_images=False)
new_cfg = replace(cfg, ingestion=new_ingestion)

8. Logging configuration

Env VarDefaultTypeMeaning
PLR_LOG_LEVELINFOStrDEBUG \

Log files:

Example:

bash# Debug logging
export PLR_LOG_LEVEL=DEBUG
python backend/main.py

Programmatic setup:

pythonfrom legal_retrieval.logging_setup import setup_logging

setup_logging(level="DEBUG", force=True)

9. Agent configuration

Env VarDefaultTypeMeaning
PLR_AGENT_POST_TOOL_REASONING_CHARS300IntMax chars buffered as post-tool CoT
PLR_AGENT_POST_TOOL_REASONING_SEGMENTS2IntMax short segments counted as reasoning
PLR_AGENT_POST_TOOL_REASONING_SHORT_CHARS80IntMax chars for a segment to be "short"

What it controls:

Example:

bash# More aggressive CoT capture
export PLR_AGENT_POST_TOOL_REASONING_SEGMENTS=5
export PLR_AGENT_POST_TOOL_REASONING_CHARS=500

10. API provider configuration

See API_PROVIDER.md for cloud mode setup.

Configuration file

File: backend/api_config.json (git-ignored)

json{
  "enabled": true,
  "provider": "openai",
  "base_url": "https://api.openai.com/v1",
  "api_key": "sk-...",
  "llm_model": "gpt-4o-mini",
  "embed_model": "text-embedding-3-small",
  "embed_dim": 0,
  "rerank_mode": "auto",
  "temperature": 0.3,
  "max_tokens": 4096,
  "timeout": 120,
  "thinking_budget": 0
}

Environment variable overrides

Env VarJSON KeyTypeMeaning
LAWYER_API_ENABLEDenabledBoolMaster switch
LAWYER_PROVIDERproviderStropenai \
LAWYER_API_BASE_URLbase_urlURLMain endpoint
LAWYER_API_KEYapi_keyStrMain API key
LAWYER_LLM_MODELllm_modelStrLLM model name
LAWYER_LLM_BASE_URL(override)URLLLM-specific endpoint
LAWYER_LLM_API_KEY(override)StrLLM-specific key
LAWYER_EMBED_MODELembed_modelStrEmbedding model name
LAWYER_EMBED_BASE_URL(override)URLEmbeddings-specific endpoint
LAWYER_EMBED_API_KEY(override)StrEmbeddings-specific key
LAWYER_EMBED_DIMembed_dimIntEmbedding dimension (0 = auto-detect)
LAWYER_RERANK_MODErerank_modeStrauto \
LAWYER_RERANK_BASE_URL(override)URLRerank-specific endpoint
LAWYER_RERANK_API_KEY(override)StrRerank-specific key
LAWYER_RERANK_MODEL(override)StrRerank model name
LAWYER_TEMPERATUREtemperatureFloatLLM temperature
LAWYER_MAX_TOKENSmax_tokensIntLLM max tokens
LAWYER_TIMEOUTtimeoutIntAPI timeout (seconds)
LAWYER_THINKING_BUDGETthinking_budgetIntAnthropic extended thinking budget (tokens)

Precedence: env vars > JSON file > code defaults

Example:

bash# Quick test with OpenAI
export LAWYER_API_ENABLED=1
export LAWYER_PROVIDER=openai
export LAWYER_API_KEY=sk-...
export LAWYER_LLM_MODEL=gpt-4o-mini

python backend/main.py

11. Workspace configuration

Active workspace

Set via API:

bashcurl -X POST http://localhost:8765/api/workspace \
  -H "Content-Type: application/json" \
  -d '{"path":"/absolute/path/to/project"}'

Or via frontend "Work in a folder" button (native directory picker).

Workspace storage

When a workspace is active:

Runtime-only (resets on restart or DELETE /api/workspace).

Per-project GPU profile

Stored in <workspace>/workspace/history.json under "gpu_profile":

json{
  "gpu_profile": {
    "residency_mode": "adaptive",
    "keep_resident_free_mb": 1024,
    "hot_min_uses": 2,
    "hot_window_seconds": 120,
    "max_resident": 2
  },
  "conversations": [ ... ],
  "working_set": [ ... ]
}

Applied automatically when workspace is selected.


12. Frontend configuration

Build-time configuration

File: frontend/.env.local (git-ignored)

VITE_API_BASE_URL=http://localhost:8765

Usage:

typescriptconst API_BASE = import.meta.env.VITE_API_BASE_URL || 'http://localhost:8765';

Runtime configuration (Electron)

File: frontend/electron/src/main/config.ts

typescriptexport const CONFIG = {
  backend: {
    port: 8765,
    host: '127.0.0.1',
    healthCheckInterval: 500,
    healthCheckTimeout: 30000,
  },
  vite: {
    port: 5173,
  },
};

13. Launcher configuration

File: backend/api_config.json (written by launcher)

See §10 API provider configuration.

Launcher-specific env vars:

Env VarDefaultMeaning
LAWYER_API_CONFIGbackend/api_config.jsonPath to API config file
LAWYER_PYTHON(auto-detected)Python interpreter for backend
LAWYER_OLLAMA_MODELlfm2.5:8bDefault Ollama model to pull

14. Configuration files reference

FilePurposeGit-ignored?
backend/api_config.jsonAPI provider settings✅ Yes
<workspace>/workspace/history.jsonPer-project chat history + GPU profile❌ No (user data)
data/conversations.dbLangGraph thread state (SQLite)❌ No (user data)
data/flags.dbCompliance flags (SQLite)❌ No (user data)
logs/legal_retrieval.logRotating log file✅ Yes
frontend/.env.localFrontend build-time config✅ Yes

15. Configuration validation

Check current config:

bashcurl http://localhost:8765/api/health

Returns:

json{
  "status": "ok",
  "service": "lawyer-assistant",
  "version": "1.0.0",
  "config": {
    "device": "cuda",
    "embedding_dim": 1024,
    "search_mode": "hybrid",
    "rerank_enabled": true,
    "gpu_manager_enabled": true,
    "workspace_active": true
  }
}

Check GPU config:

bashcurl http://localhost:8765/api/gpu/status

Check API provider:

bashcurl http://localhost:8765/api/providers/status

16. Common configuration scenarios

Scenario 1: Low-VRAM GPU (4GB)

bashexport PLR_GPU_RESIDENCY_MODE=eager
export PLR_GPU_MAX_RESIDENT=1
export PLR_EMBEDDING_BATCH_SIZE=16
export PLR_RERANKER_BATCH_SIZE=16
export PLR_GPU_HEADROOM_MB=512

Scenario 2: High-VRAM GPU (24GB+)

bashexport PLR_GPU_RESIDENCY_MODE=persistent
export PLR_GPU_MAX_RESIDENT=3
export PLR_EMBEDDING_BATCH_SIZE=64
export PLR_RERANKER_BATCH_SIZE=64
export PLR_LLM_NUM_GPU=-1  # use GPU for LLM too

Scenario 3: CPU-only (no GPU)

bashexport PLR_DEVICE=cpu
export PLR_EMBEDDING_USE_FP16=False
export PLR_RERANKER_USE_FP16=False
export PLR_GPU_MANAGER_ENABLED=0
export PLR_EMBEDDING_BATCH_SIZE=4  # smaller for RAM

Scenario 4: Cloud API mode (no local models)

bashexport LAWYER_API_ENABLED=1
export LAWYER_PROVIDER=openai
export LAWYER_API_KEY=sk-...
export LAWYER_LLM_MODEL=gpt-4o-mini
export LAWYER_EMBED_MODEL=text-embedding-3-small

Scenario 5: Debugging

bashexport PLR_LOG_LEVEL=DEBUG
export PLR_GPU_MANAGER_ENABLED=1  # watch GPU logs
python backend/main.py 2>&1 | tee debug.log

17. Configuration troubleshooting

SymptomLikely CauseFix
"Config not loaded"Typo in env var nameCheck PLR_ prefix
API key rejectedWrong key or expiredCheck api_config.json
GPU not usedDevice set to CPUCheck PLR_DEVICE
Slow ingestionBatch size too smallIncrease PLR_EMBEDDING_BATCH_SIZE
OOM during searchBatch size too largeDecrease batch sizes
Logs not createdLogs dir missingmkdir logs/
Workspace not activeNo folder selectedPOST /api/workspace

DocCoverage
GPU_MANAGEMENT.mdGPU configuration deep dive
API_PROVIDER.mdCloud mode configuration
WORKSPACE.mdWorkspace configuration
BACKEND.mdRuntime pipeline config
TROUBLESHOOTING.mdConfig-related failures
DEVELOPMENT.mdDev environment config

Questions, answered

Short, self-contained answers about this guide.

Where is configuration defined?

Configuration lives in backend/legal_retrieval/config.py with environment-variable overrides (for example model directories, device, and chunk size). API-provider settings come from a git-ignored api_config.json written by the launcher.

What is the default chunk size?

Documents are chunked near a target of 480 tokens, with section respect so headers are never split across chunks. The value is configurable through the config file or environment variables.

How do I change retrieval top_k or disable reranking?

Set top_k in config, or override per request (validated 1–100). Reranking is on by default and can be disabled with skip_rerank (or --no-rerank on the CLI); the pipeline editor can also toggle it at runtime.