The Problem

Enterprises that need to expose document‑wide question‑answering across many customers run into two issues: (1) keeping each tenant’s data isolated while sharing the same retrieval infrastructure, and (2) handling the wide variety of file formats (PDF, Word, images, tables, formulas) without building a custom parser stack for every use‑case.

What This Does

rag-api delivers a multi‑tenant Retrieval‑Augmented Generation service that couples LightRAG (graph‑based retrieval) with RAG‑Anything (multimodal parsing). The code base is organized around three domains:

  • src/ – core engine (config, multi‑tenant manager, parsers, rate‑limiter, metrics).
  • src/multi_tenant.py creates per‑tenant LLM and embedding functions and is the hub for tenant isolation.
  • src/smart_parser_selector.py picks the best parser (DeepSeek‑OCR, MinerU, Docling) using the complexity scorer in src/document_complexity.py.
  • src/rag.py runs the LightRAG retrieval pipeline.
  • api/ – FastAPI request handlers (documents.py, insert.py, query.py, etc.) that translate HTTP calls into engine actions.
  • scripts/ – operational helpers; scripts/migrate_tenant.py is the only CLI entry that performs tenant migration.

The top‑level main.py starts the FastAPI server, wiring the router defined in api/__init__.py to the engine modules.

How It Is Wired

  1. Startup – python main.py (or the Docker entrypoint) creates the FastAPI app.
  2. Request flow – an incoming HTTP call hits a handler in api/. For a document upload the path is: api/insert.insert_document → src/multi_tenant.get_tenant_lightrag (called from 9 places) → src/rag.process_document_task → parser selection (src/smart_parser_selector.select_parser) → concrete parser (src/deepseek_ocr_client, src/mineru_client, or src/docling_client) → vector store write (Qdrant via src/rate_limiter.acquire and src/tenant_config) → task persistence (api/task_store).

The most fan‑in functions are src/logger (22 imports) and api/insert (high instability). Changing src/logger has the widest blast radius.

  1. External effects – File system – parsers open PDFs/Docs (open without context manager in src/deepseek_ocr_client.py and src/document_complexity.py). Network – OCR calls (requests.get in deepseek_ocr_client), MinerU remote API (src/mineru_client), and tenant config fetches. * Database – task CRUD (api/task_store) writes to Redis/DragonflyDB; tenant config reads/writes JSON files (src/tenant_config.py).
  1. Rate limiting – every outbound call passes through src/rate_limiter.acquire, which is invoked from 8 locations, making it a critical throttling point.
  1. Metrics – src/metrics.py records response times and error rates; these are called from the monitoring endpoints in api/monitor.py.

The internal call graph shows 290 resolved intra‑repo edges; the most reused utilities (get_tenant_config_manager, DocumentFeatures, ParseOptions) appear in 5–7 places each, indicating they are natural extension points.

How To Use It

# Clone the repo
git clone https://github.com/moses-y/rag-api
cd rag-api

# Build the container (uses uv for dependency resolution)
docker build -f Dockerfile -t rag-api:latest .

# Run the stack (FastAPI + Qdrant vector store)
docker compose up -d   # uses docker-compose.yml

# The API is now reachable at http://localhost:8000
# Swagger UI: http://localhost:8000/docs

Configuration – tenant isolation and parser keys are read from files under src/tenant_config.py and environment variables referenced there (e.g., DEEPSEEK_API_KEY). No secret files are committed; provide them at container start.

CLI migrations – to migrate an existing tenant configuration:

python scripts/migrate_tenant.py --old-id old_tenant --new-id new_tenant

The script uses src/multi_tenant to copy vector data and task records.

Real‑World Use

A SaaS provider can spin up a single rag-api service behind a tenant‑aware gateway. When a client uploads a contract PDF, the service routes the request to api/insert, the smart selector chooses DeepSeek‑OCR for complex layout, stores embeddings in Qdrant, and later answers queries via api/query using LightRAG’s hybrid graph retrieval. All tenant data remains isolated by the per‑tenant manager in src/multi_tenant.py.

Code Health & Issues

  • HIGH – Pin third‑party GitHub Actions to a commit SHA (.github/workflows/ci-basic.yml).
  • HIGH – No LICENSE file – redistribution rights undefined.
  • MEDIUM – GITHUB_TOKEN permissions not declared (.github/workflows/ci-basic.yml).
  • MEDIUM – No Dependabot/Renovate configuration.
  • MEDIUM – Base Docker image not pinned by digest (Dockerfile).
  • MEDIUM – No dependency‑vulnerability scan in CI.
  • MEDIUM – Container runs as root; no non‑root USER directive.
  • MEDIUM – Test coverage is minimal (1 test file vs 29 source files).
  • LOW – Workflow jobs lack timeout-minutes.

Additional static findings: deep nesting (max depth 9) in src/multi_tenant.py and related modules, file handles opened without with, and broad except: clauses in request handlers. These increase maintenance risk and should be refactored.

The Bottom Line

rag-api provides a functional, container‑ready multi‑tenant RAG service with solid architecture (FastAPI + LightRAG + auto‑parser selection). It is usable out of the box but needs hardening: add a license, tighten CI security, improve test coverage, and refactor the most complex modules before extending it in production. Suitable for teams comfortable with Python, Docker, and FastAPI who can invest in the outlined cleanup.