A production-oriented RAG (Retrieval-Augmented Generation) API built with Python, FastAPI, ChromaDB, OpenAI and Redis.
Upload PDFs, ask questions in natural language and get answers grounded only in the uploaded documents.
- PDF ingestion — upload a PDF, extract text, split it into chunks and index it in ChromaDB.
- Semantic search — query embeddings retrieve the most relevant chunks.
- Conversational Q&A — multi-turn chat sessions with history stored in Redis.
- Source attribution — every answer cites the source PDF filenames.
- Containerized — run locally with Docker Compose (app + ChromaDB + Redis).
- CI/CD — GitHub Actions runs linting, type checking and tests on every push.
| Layer | Technology |
|---|---|
| Language | Python 3.11+ |
| Framework | FastAPI |
| Vector DB | ChromaDB |
| Embeddings | OpenAI text-embedding-3-small |
| LLM | OpenAI gpt-4o-mini |
| Cache / Chat history | Redis |
| PDF parsing | pypdf |
| Chunking | langchain-text-splitters |
| Testing | pytest, httpx |
| Lint / Type | ruff, mypy |
| CI/CD | GitHub Actions |
ai-pdf-assistant-api/
├── app/
│ ├── config.py # Pydantic settings
│ ├── main.py # FastAPI entrypoint
│ ├── dependencies.py # Shared helpers
│ ├── db/
│ │ ├── chroma_client.py
│ │ └── redis_client.py
│ ├── models/
│ │ └── schemas.py
│ ├── routers/
│ │ ├── documents.py # upload / list / delete
│ │ └── chat.py # ask / delete session
│ └── services/
│ ├── pdf_service.py
│ ├── embedding_service.py
│ ├── llm_service.py
│ └── chat_service.py
├── tests/
├── .github/workflows/ci.yml
├── Dockerfile
├── docker-compose.yml
├── pyproject.toml
└── README.md
- Python 3.11+
- Docker + Docker Compose (recommended)
- An OpenAI API key
- Copy the environment file and add your OpenAI key:
cp .env.example .env
# edit .env and set OPENAI_API_KEY- Start the services:
docker compose up --buildThe API will be available at http://localhost:8001 and the docs at http://localhost:8001/docs.
- Create and activate a virtual environment:
python -m venv .venv
source .venv/bin/activate # on Windows: .venv\Scripts\activate- Install dependencies:
pip install -e ".[dev]"- Start ChromaDB and Redis:
docker run -d -p 8000:8000 chromadb/chroma:latest
docker run -d -p 6379:6379 redis:7-alpine- Configure
.env:
cp .env.example .env
# set OPENAI_API_KEY, CHROMA_HOST=localhost, REDIS_URL=redis://localhost:6379/0- Run the server:
uvicorn app.main:app --reloadcurl -X POST "http://localhost:8001/documents" \
-H "accept: application/json" \
-F "file=@contract.pdf"Response:
{
"id": "a1b2c3d4...",
"filename": "contract.pdf",
"chunks": 12,
"created_at": "2026-06-21T17:34:00"
}curl "http://localhost:8001/documents"curl -X POST "http://localhost:8001/chat" \
-H "Content-Type: application/json" \
-d '{
"session_id": "session-1",
"question": "What is the main topic of the document?"
}'Response:
{
"session_id": "session-1",
"answer": "The document describes the terms of a service agreement...",
"sources": ["contract.pdf"]
}curl -X DELETE "http://localhost:8001/chat/session-1"| Variable | Description | Default |
|---|---|---|
OPENAI_API_KEY |
OpenAI API key | required |
OPENAI_EMBEDDING_MODEL |
Embedding model | text-embedding-3-small |
OPENAI_CHAT_MODEL |
Chat model | gpt-4o-mini |
CHROMA_HOST |
ChromaDB host | localhost |
CHROMA_PORT |
ChromaDB port | 8000 |
CHROMA_COLLECTION |
ChromaDB collection name | documents |
REDIS_URL |
Redis URL | redis://localhost:6379/0 |
CHUNK_SIZE |
Text chunk size | 500 |
CHUNK_OVERLAP |
Chunk overlap | 50 |
TOP_K |
Number of chunks retrieved | 5 |
MAX_FILE_SIZE |
Max upload size in bytes | 10485760 (10 MB) |
ruff check app tests
ruff format app testsmypy apppytest- Support for local LLMs via Ollama
- Streaming chat responses
- Rate limiting per user/session
- Persistent document metadata in PostgreSQL
- WebSocket chat endpoint
MIT © Francesco Nocerino