Skip to content

Repository files navigation

RAG Web Application

An async Retrieval-Augmented Generation (RAG) web application — upload documents, ask questions, get streaming answers grounded in your content.

Warning

This application is a prototype.


How It Works

Ingest: Upload a document → background worker parses it → chunks it → generates hypothetical questions per chunk (HyPE indexing) → embeds and stores everything in Qdrant. Progress streams back to the UI over SSE.

Chat: Send a message → retrieve relevant chunks from Qdrant → optionally rerank by cross-encoder score → stream an LLM response back over SSE → persist the exchange to the database.

Stack

Layer Technology
API FastAPI — async, class-based views, JWT auth (httpOnly cookies)
Task scheduling APScheduler
Vector store Qdrant with FastEmbed (BAAI/bge-small-en-v1.5)
Document parsing pypdf, python-docx, openpyxl — PDF, DOCX, XLSX, Markdown
Reranking FastEmbed cross-encoder (Xenova/ms-marco-MiniLM-L-6-v2)
Database SQLite (dev) / PostgreSQL 17 (Docker)
LLM providers Groq, OpenRouter — tenacity retry + SSE streaming
Frontend React 19, TypeScript, Vite 8, TailwindCSS 4, TanStack Query 5, react-router-dom v7

Quick Start

Prerequisites

  • Python 3.13+ and uv
  • Node.js 20+

Qdrant runs in local-path mode by default — no separate server required for development.

1. Install

git clone https://github.com/froggobytes0460/RAGWebApp.git
cd RAGWebApp
uv sync
uv run pre-commit install
cp .env.example .env
# Edit .env — at minimum set LLM__API_KEY and AUTH__JWT_SECRET

2. Migrate database

uv run migrate-run        # shortcut via uv_tasks.py
# or: uv run alembic upgrade head

3. Run

# API (FastAPI dev server, logs to logs/api.log)
NO_COLOR=1 uv run fastapi dev backend/api > logs/api.log 2>&1 &

# Frontend dev server (proxies /api → :8000)
cd frontend && npm install && npm run dev

4. Test

# Backend — 80% coverage threshold enforced
uv run test-all           # shortcut via uv_tasks.py
# or: uv run pytest -q --tb=short --no-header

# Lint + type-check
uv run pre-commit run --all-files

Docker (Production)

docker compose up --build   # nginx + api + qdrant + postgres
docker compose down

Services: nginx (port 80) → api (:8000) + qdrant + postgres:17. Model caches (hf_cache, docling_cache) are persisted in named volumes.

Add to .env.docker to switch from SQLite/local Qdrant to the containerised backends:

DATABASE__URI=postgresql+asyncpg://user:password@postgres:5432/ragdb
VECTOR_STORE__URL_OR_PATH=http://qdrant:6333/

Environment Variables

Full reference in .env.example and backend/core/config.py.

Variable Default Notes
AUTH__JWT_SECRET (required) Secret used to sign JWT tokens — keep private
AUTH__JWT_ALGORITHM HS256 JWT signing algorithm
AUTH__ACCESS_TOKEN_TTL_SECONDS 900 Access token lifetime in seconds (default: 15 min)
AUTH__REFRESH_TOKEN_TTL_SECONDS 2592000 Refresh token lifetime in seconds (default: 30 days)
AUTH__COOKIE_SECURE true Set Secure flag on auth cookies; disable only in HTTP dev
AUTH__COOKIE_SAMESITE lax SameSite policy: lax, strict, or none
AUTH__GOOGLE_CLIENT_ID (unset) Google OAuth2 client ID — required to enable Google login
AUTH__GOOGLE_CLIENT_SECRET (unset) Google OAuth2 client secret
AUTH__GOOGLE_REDIRECT_URI (unset) Callback URI registered in Google Cloud Console
LLM__API_KEY (required) API key for the configured LLM provider
LLM__PROVIDER groq LLM provider (groq or openrouter)
LLM__MODEL_NAME (required) Model ID for the chosen provider
LLM__TEMPERATURE 0.2 Sampling temperature (0.0–1.0)
LLM__MAX_OUTPUT_TOKEN (required) Maximum tokens in the LLM reply
DATABASE__URI sqlite+aiosqlite:///./rag.db Switch to postgresql+asyncpg://... in Docker
VECTOR_STORE__URL_OR_PATH ./.qdrant_local/ Set to http://qdrant:6333/ in Docker
VECTOR_STORE__EMBEDDING_MODEL BAAI/bge-small-en-v1.5 FastEmbed model; must match VECTOR_STORE__VECTOR_SIZE
VECTOR_STORE__VECTOR_SIZE 384 Must match the chosen embedding model's output dimension
VECTOR_STORE__API_KEY (unset) Required when connecting to Qdrant Cloud
TEXT_CHUNK__CHUNK_SIZE (required) Token budget per chunk
TEXT_CHUNK__CHUNK_OVERLAP (required) Overlap tokens between chunks (must be < CHUNK_SIZE)
SEARCH__TOP_K (required) Number of chunks to retrieve per query
SEARCH__SEARCH_TYPE similarity similarity, mmr, or similarity_score_threshold
RERANK__ENABLED true Enable cross-encoder reranking after retrieval
RERANK__MODEL_NAME Xenova/ms-marco-MiniLM-L-6-v2 FastEmbed cross-encoder model
INGEST__MAX_FILE_SIZE 50 Maximum upload size in MB
INGEST__WORKER_CONCURRENCY 4 Parallel ingestion workers
INGEST__HYPE_QUESTIONS_PER_CHUNK 3 Hypothetical questions generated per chunk (HyPE indexing)

API Reference

Authentication

JWT tokens issued as httpOnly cookies.

Method Path Description
POST /api/v1/auth/register Register a new user (201)
POST /api/v1/auth/login Login; sets access_token + refresh_token cookies (200)
POST /api/v1/auth/refresh Rotate refresh token (204)
POST /api/v1/auth/logout Clear auth cookies (204)
GET /api/v1/auth/me Current authenticated user (200)
GET /api/v1/auth/google Initiate Google OAuth flow (302)
GET /api/v1/auth/google/callback Google OAuth callback (302)

Sessions

Requires authentication

Method Path Description
POST /api/v1/chats/sessions Create a new chat session (201)
GET /api/v1/chats/sessions List sessions for the authenticated user (200)
DELETE /api/v1/chats/sessions/{session_id} Delete a session and all its data (204)

Documents & Messages

Requires authentication

Method Path Description
POST /api/v1/chats/{session_id}/documents Upload and ingest a document (202)
GET /api/v1/chats/{session_id}/documents List documents for a session (200)
GET /api/v1/chats/{session_id}/documents/jobs/{job_id}/progress SSE stream — ingestion progress
DELETE /api/v1/chats/{session_id}/documents/{filename} Delete a document (202)
POST /api/v1/chats/{session_id}/messages Send a message; stream LLM reply as SSE (201)
GET /api/v1/chats/{session_id}/messages Retrieve message history (200)

Health

Method Path Description
GET /api/health Liveness check — {"status", "version"}, always HTTP 200
GET /api/health/deep Dependency check — {"status", "version", "dependencies": {"database", "vector_store"}} each with status and latency_ms; 503 if any degraded.

Repository Structure

├── backend/
│   ├── api/                        # FastAPI routes, schemas, auth, dependencies, app state
│   └── core/
│       ├── auth/                   # JWT token helpers, password hashing, OAuth utilities
│       ├── llms/                   # Groq + OpenRouter clients, HyPE question generation, RAG prompt template
│       ├── config.py               # Pydantic BaseSettings singleton
│       ├── ingest.py               # pypdf / python-docx / openpyxl loader + binary-head validation
│       ├── chunking.py             # Tokenizer-aware text splitter (LRU-cached)
│       ├── reranker.py             # FastEmbed cross-encoder reranking
│       ├── vector_store.py         # Qdrant wrapper (FastEmbed)
│       ├── ingestion_worker.py     # Background async ingest worker (HyPE + chunking + upsert)
│       ├── database.py             # Async SQLAlchemy engine + session factory
│       └── models.py               # SQLModel tables (User, ChatSession, ChatMessage, IngestionJob)
├── frontend/
│   └── src/
│       ├── App.tsx                 # Root component; all app state lives here
│       ├── api.ts                  # Typed API client (fetch + SSE)
│       ├── types.ts                # Shared TypeScript types
│       └── helpers.ts              # uid, formatSize, ageText, badge utilities
├── alembic/                        # Database migrations
├── tests/
│   ├── api/                        # Integration tests (httpx AsyncClient)
│   └── core/                       # Unit tests — ingest, chunking, vector store, LLM clients
├── nginx/                          # Reverse-proxy config
├── .github/workflows/              # CI: backend
├── Dockerfile                      # Multi-stage build
└── docker-compose.yml              # Full production stack

CI

Workflow Trigger Gates
backend.yaml Changes to backend/, tests/, pyproject.toml black → basedpyright → pytest ≥ 80% coverage

Security

  • JWT auth — access + refresh tokens issued as httpOnly, SameSite=lax cookies; refresh rotation on every use.
  • Extension spoofing protection — binary-head validation runs before any file is parsed.
  • Double upload-size enforcementContent-Length header check in middleware, plus streaming chunk accumulation in the route handler.
  • API key validation — Groq and Qdrant Cloud keys validated against strict regex patterns at startup.

About

An async retrieval augmented generator (RAG) web application. Answers questions solely based on provided documents.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages