Irminsul / README.MD
MukulRay's picture
chore: code cleanup, add .env.example, update README
c8b552c
Raw
History Blame
5.98 kB
# Irminsul
> Fine-tuned Llama 3.1 8B Β· QLoRA Β· Pinecone RAG Β· FastAPI Β· Azure Container Apps
A full end-to-end LLMOps serving stack β€” from a QLoRA fine-tuned model running in 4-bit NF4 on consumer hardware, through a retrieval-augmented generation pipeline, to a containerized API deployed on Azure. Built to be production-shaped, not just a demo.
**[β†’ Live Demo](https://mukulray1603.github.io/Irminsul/demo.html)**
---
## About Irminsul
Most LLM projects stop at inference. This one goes further:
- **Fine-tuned model** β€” Llama 3.1 8B fine-tuned with QLoRA (rank 16, lr 2e-4) on a custom dataset, merged and served locally in 4-bit NF4 quantization on an RTX 3060 6GB
- **RAG pipeline** β€” Documents ingested, chunked, embedded with `sentence-transformers/all-MiniLM-L6-v2` (fully local, zero API cost), and stored in Pinecone. Retrieval is semantic, top-k configurable at query time
- **Serving layer** β€” FastAPI with async lifespan model loading, typed Pydantic request/response models, CORS, health check, and a clean browser UI served from the same process
- **Containerized** β€” Dockerfile built for slim Python 3.12, model loaded at runtime via env-configurable path (not baked in)
- **Cloud-ready** β€” One-shot Azure deployment via ACR + Container Apps, with Pinecone key injected as a secret
- **Domain knowledge** β€” RAG corpus built around Genshin Impact lore, character builds, and elemental mechanics, serving as a rich real-world knowledge base for retrieval evaluation
---
## Architecture
```
User query
β”‚
β–Ό
FastAPI /generate
β”‚
β”œβ”€β”€ Embed query (sentence-transformers, local)
β”‚ β”‚
β”‚ β–Ό
β”‚ Pinecone β€” semantic search β†’ top-k chunks
β”‚ β”‚
β–Ό β–Ό
LangChain RetrievalQA
β”‚
β–Ό
Llama 3.1 8B (QLoRA fine-tuned, 4-bit NF4)
β”‚
β–Ό
Grounded answer + source attribution
```
---
## Stack
| Layer | Technology |
|---|---|
| Base model | Llama 3.1 8B Instruct |
| Fine-tuning | QLoRA via PEFT (r=16, Ξ±=32, lr=2e-4) |
| Quantization | BitsAndBytes 4-bit NF4, bfloat16 compute |
| Embeddings | sentence-transformers/all-MiniLM-L6-v2 |
| Vector DB | Pinecone (serverless, cosine similarity) |
| RAG chain | LangChain RetrievalQA |
| Serving | FastAPI + Uvicorn |
| Containerization | Docker (python:3.12-slim) |
| Cloud | Azure Container Apps + ACR |
---
## Quickstart
```bash
# 1. Clone and set up environment
git clone https://github.com/MukulRay1603/Irminsul.git
cd Irminsul
python -m venv venv && source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements.txt
# 2. Configure environment
cp .env.example .env
# Fill in PINECONE_API_KEY in .env
# 3. Add your fine-tuned model
# Place merged model at: ./models/merged/exp2_lr2e-4_r16
# Or update MODEL_PATH in .env to point to your model
# 4. Ingest documents
python ingest.py --dir ./docs --chunk-size 300 --chunk-overlap 40
# 5. Start the server
uvicorn main:app --reload --port 8000
# UI available at http://localhost:8000
# API docs at http://localhost:8000/docs
```
---
## API
| Method | Endpoint | Description |
|---|---|---|
| `GET` | `/` | Browser UI |
| `GET` | `/health` | Model load status |
| `POST` | `/generate` | RAG query β†’ grounded answer |
| `POST` | `/ingest` | Ingest docs from local directory |
**Example:**
```bash
curl -X POST http://localhost:8000/generate \
-H "Content-Type: application/json" \
-d '{"query": "What weapons should Hu Tao use on a budget?", "top_k": 3}'
```
```json
{
"answer": "For Hu Tao on a budget, Dragon's Bane is the strongest F2P option β€” it scales with Elemental Mastery and deals significant bonus damage on vaporized hits. White Tassel is the best 3-star alternative for pure Normal Attack scaling.",
"sources": ["docs/character_builds.md"],
"latency_ms": 4821.3
}
```
---
## Memory profile (RTX 3060 6GB)
| Component | VRAM |
|---|---|
| Llama 3.1 8B @ 4-bit NF4 | ~4.5 GB |
| all-MiniLM-L6-v2 embedder | ~90 MB |
| Inference headroom | ~1.2 GB |
Running the embedder on CPU frees ~90MB if needed β€” set `device_map="cpu"` in `rag.py`.
---
## Deploy to Azure
```bash
export PINECONE_API_KEY=your_key
chmod +x deploy_azure.sh
./deploy_azure.sh
```
The script provisions a resource group, builds and pushes the image via ACR Tasks (no local Docker build needed), creates a Container Apps environment, and deploys with the Pinecone key injected as a secret. Prints the live HTTPS endpoint on completion.
**Model in Azure:** The merged model (~16GB) isn't baked into the image. Recommended approach: mount from Azure Blob Storage as a volume for cheapest cold start on student credits.
---
## Project structure
```
Irminsul/
β”œβ”€β”€ main.py # FastAPI app β€” endpoints, lifespan, CORS
β”œβ”€β”€ rag.py # Model loading, 4-bit config, LangChain RAG chain
β”œβ”€β”€ embedder.py # sentence-transformers singleton wrapper
β”œβ”€β”€ ingest.py # Doc loader β†’ chunker β†’ Pinecone upsert
β”œβ”€β”€ index.html # Browser UI (dark theme, query history, source display)
β”œβ”€β”€ Dockerfile
β”œβ”€β”€ deploy_azure.sh # One-shot Azure Container Apps deploy
β”œβ”€β”€ requirements.txt
β”œβ”€β”€ .env.example
└── docs/ # Corpus + GitHub Pages demo
└── demo.html
```
---
## What's next
- [ ] Swap naive word chunker for `MarkdownHeaderTextSplitter` for better retrieval precision
- [ ] Add metadata filtering to Pinecone queries (filter by character, content type)
- [ ] Streaming response via SSE for lower perceived latency
- [ ] Expand corpus β€” per-character deep dives with stat thresholds and rotation guides
- [ ] CI/CD pipeline β€” GitHub Actions β†’ ACR build β†’ Container Apps deploy on push
---
Built while learning the full MLOps lifecycle β€” fine-tuning, quantization, retrieval, serving, and cloud deployment β€” on consumer hardware. Every component chosen deliberately, not for hype.