Spaces:
Sleeping
Sleeping
Commit ยท
3a21631
0
Parent(s):
Initial deployment
Browse files- .gitattributes +1 -0
- .gitignore +7 -0
- Dockerfile +32 -0
- HF_DEPLOYMENT_GUIDE.md +275 -0
- README.md +46 -0
- app.py +202 -0
- harrypotter.txt +3 -0
- requirements.txt +3 -0
.gitattributes
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
*.txt filter=lfs diff=lfs merge=lfs -text
|
.gitignore
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
__pycache__/
|
| 2 |
+
*.pyc
|
| 3 |
+
*.pyo
|
| 4 |
+
.env
|
| 5 |
+
.DS_Store
|
| 6 |
+
student_manual_faiss/
|
| 7 |
+
*.log
|
Dockerfile
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Dockerfile for KUMPAS RAG โ Hugging Face Spaces
|
| 2 |
+
FROM python:3.11-slim
|
| 3 |
+
|
| 4 |
+
# System dependencies for faiss-cpu and sentence-transformers
|
| 5 |
+
RUN apt-get update && apt-get install -y \
|
| 6 |
+
build-essential \
|
| 7 |
+
curl \
|
| 8 |
+
git \
|
| 9 |
+
&& rm -rf /var/lib/apt/lists/*
|
| 10 |
+
|
| 11 |
+
# Working directory
|
| 12 |
+
WORKDIR /app
|
| 13 |
+
|
| 14 |
+
# Copy requirements first (Docker layer caching โ faster rebuilds)
|
| 15 |
+
COPY requirements.txt .
|
| 16 |
+
RUN pip install --no-cache-dir -r requirements.txt
|
| 17 |
+
|
| 18 |
+
# Copy application files
|
| 19 |
+
COPY app.py .
|
| 20 |
+
|
| 21 |
+
# Copy your Harry Potter text file
|
| 22 |
+
COPY harrypotter.txt .
|
| 23 |
+
|
| 24 |
+
# HF Spaces runs on port 7860
|
| 25 |
+
EXPOSE 7860
|
| 26 |
+
|
| 27 |
+
# Predownload the embedding model at build time
|
| 28 |
+
# (so the first request isn't slow)
|
| 29 |
+
RUN python -c "from sentence_transformers import SentenceTransformer; SentenceTransformer('all-MiniLM-L6-v2')"
|
| 30 |
+
|
| 31 |
+
# Start the server
|
| 32 |
+
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860", "--workers", "1"]
|
HF_DEPLOYMENT_GUIDE.md
ADDED
|
@@ -0,0 +1,275 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Deploying KUMPAS RAG to Hugging Face Spaces
|
| 2 |
+
## Complete Step-by-Step Guide
|
| 3 |
+
|
| 4 |
+
---
|
| 5 |
+
|
| 6 |
+
## What You Need Before Starting
|
| 7 |
+
- [ ] A Hugging Face account (free) โ huggingface.co
|
| 8 |
+
- [ ] Your Groq API key โ console.groq.com
|
| 9 |
+
- [ ] Git installed on your computer
|
| 10 |
+
- [ ] Your `harrypotter.txt` file (from Google Drive)
|
| 11 |
+
|
| 12 |
+
---
|
| 13 |
+
|
| 14 |
+
## PHASE 1 โ Hugging Face Account & Token
|
| 15 |
+
|
| 16 |
+
### 1.1 Create Account
|
| 17 |
+
1. Go to **https://huggingface.co**
|
| 18 |
+
2. Click **Sign Up** โ fill in username, email, password
|
| 19 |
+
3. Verify your email
|
| 20 |
+
|
| 21 |
+
### 1.2 Get Your Access Token
|
| 22 |
+
1. Click your profile picture (top right) โ **Settings**
|
| 23 |
+
2. Left sidebar โ **Access Tokens**
|
| 24 |
+
3. Click **New token**
|
| 25 |
+
- Name: `kumpas-deploy`
|
| 26 |
+
- Role: **Write**
|
| 27 |
+
4. Click **Generate token** โ **Copy it** (you'll need it in a moment)
|
| 28 |
+
|
| 29 |
+
---
|
| 30 |
+
|
| 31 |
+
## PHASE 2 โ Create the Space
|
| 32 |
+
|
| 33 |
+
### 2.1 New Space
|
| 34 |
+
1. Click your profile picture โ **New Space**
|
| 35 |
+
2. Fill in:
|
| 36 |
+
- **Owner**: your username
|
| 37 |
+
- **Space name**: `kumpas-rag` (URL will be `yourname-kumpas-rag.hf.space`)
|
| 38 |
+
- **License**: MIT
|
| 39 |
+
- **SDK**: **Docker** โ IMPORTANT: choose Docker, not Gradio/Streamlit
|
| 40 |
+
- **Visibility**: Public (free) or Private (requires Pro)
|
| 41 |
+
3. Click **Create Space**
|
| 42 |
+
|
| 43 |
+
You'll land on an empty space page โ that's normal.
|
| 44 |
+
|
| 45 |
+
---
|
| 46 |
+
|
| 47 |
+
## PHASE 3 โ Set Up the Secret (Groq API Key)
|
| 48 |
+
|
| 49 |
+
Do this BEFORE pushing code, so it's available when the app builds.
|
| 50 |
+
|
| 51 |
+
1. In your Space page โ **Settings** tab (top right of the space)
|
| 52 |
+
2. Scroll down to **Repository secrets**
|
| 53 |
+
3. Click **New secret**
|
| 54 |
+
- **Name**: `GROQ_API_KEY` โ must be exactly this name
|
| 55 |
+
- **Value**: paste your Groq API key (`gsk_...`)
|
| 56 |
+
4. Click **Add secret**
|
| 57 |
+
|
| 58 |
+
---
|
| 59 |
+
|
| 60 |
+
## PHASE 4 โ Prepare Files on Your Computer
|
| 61 |
+
|
| 62 |
+
### 4.1 Your folder should look like this:
|
| 63 |
+
```
|
| 64 |
+
kumpas-hf-space/
|
| 65 |
+
โโโ app.py โ from the zip
|
| 66 |
+
โโโ Dockerfile โ from the zip
|
| 67 |
+
โโโ requirements.txt โ from the zip
|
| 68 |
+
โโโ README.md โ from the zip
|
| 69 |
+
โโโ .gitignore โ from the zip
|
| 70 |
+
โโโ harrypotter.txt โ YOU add this (download from Google Drive)
|
| 71 |
+
```
|
| 72 |
+
|
| 73 |
+
### 4.2 Download harrypotter.txt from Google Drive
|
| 74 |
+
1. Open Google Drive โ find `harrypotter.txt`
|
| 75 |
+
2. Right-click โ **Download**
|
| 76 |
+
3. Move the downloaded file into your `kumpas-hf-space/` folder
|
| 77 |
+
|
| 78 |
+
---
|
| 79 |
+
|
| 80 |
+
## PHASE 5 โ Push to Hugging Face via Git
|
| 81 |
+
|
| 82 |
+
Open your terminal (Command Prompt on Windows, Terminal on Mac/Linux).
|
| 83 |
+
|
| 84 |
+
### 5.1 Navigate to your folder
|
| 85 |
+
```bash
|
| 86 |
+
cd path/to/kumpas-hf-space
|
| 87 |
+
```
|
| 88 |
+
|
| 89 |
+
### 5.2 Install Git LFS (for large files like harrypotter.txt)
|
| 90 |
+
```bash
|
| 91 |
+
# Windows (if not installed):
|
| 92 |
+
# Download from https://git-lfs.com
|
| 93 |
+
|
| 94 |
+
# Mac:
|
| 95 |
+
brew install git-lfs
|
| 96 |
+
|
| 97 |
+
# Ubuntu/Linux:
|
| 98 |
+
sudo apt-get install git-lfs
|
| 99 |
+
|
| 100 |
+
# Then enable it:
|
| 101 |
+
git lfs install
|
| 102 |
+
```
|
| 103 |
+
|
| 104 |
+
### 5.3 Initialize and push
|
| 105 |
+
```bash
|
| 106 |
+
# Initialize git
|
| 107 |
+
git init
|
| 108 |
+
|
| 109 |
+
# Track large text files with LFS
|
| 110 |
+
git lfs track "*.txt"
|
| 111 |
+
git add .gitattributes
|
| 112 |
+
|
| 113 |
+
# Add all your files
|
| 114 |
+
git add .
|
| 115 |
+
git commit -m "Initial KUMPAS RAG deployment"
|
| 116 |
+
|
| 117 |
+
# Connect to your HF Space
|
| 118 |
+
# Replace YOUR_USERNAME with your actual HF username
|
| 119 |
+
git remote add origin https://YOUR_USERNAME:YOUR_HF_TOKEN@huggingface.co/spaces/YOUR_USERNAME/kumpas-rag
|
| 120 |
+
|
| 121 |
+
# Push!
|
| 122 |
+
git push -u origin main
|
| 123 |
+
```
|
| 124 |
+
|
| 125 |
+
> **Example:** If your username is `jdelacruz` and your token starts with `hf_abc`:
|
| 126 |
+
> ```
|
| 127 |
+
> git remote add origin https://jdelacruz:hf_abc123@huggingface.co/spaces/jdelacruz/kumpas-rag
|
| 128 |
+
> ```
|
| 129 |
+
|
| 130 |
+
---
|
| 131 |
+
|
| 132 |
+
## PHASE 6 โ Watch It Build
|
| 133 |
+
|
| 134 |
+
1. Go back to your Space page on huggingface.co
|
| 135 |
+
2. Click the **Logs** tab โ you'll see the Docker build in real time:
|
| 136 |
+
|
| 137 |
+
```
|
| 138 |
+
Step 1/10 : FROM python:3.11-slim
|
| 139 |
+
Step 2/10 : RUN apt-get update...
|
| 140 |
+
Step 3/10 : COPY requirements.txt .
|
| 141 |
+
Step 4/10 : RUN pip install... โ takes 3-5 minutes
|
| 142 |
+
Step 5/10 : COPY app.py .
|
| 143 |
+
Step 6/10 : COPY harrypotter.txt .
|
| 144 |
+
Step 7/10 : RUN python -c "...SentenceTransformer..." โ downloads model
|
| 145 |
+
...
|
| 146 |
+
```
|
| 147 |
+
|
| 148 |
+
3. After the build succeeds (~5-8 minutes), the app starts:
|
| 149 |
+
```
|
| 150 |
+
INFO: Loading embeddings model...
|
| 151 |
+
INFO: Building FAISS index from harrypotter.txt... โ first run only, ~2 min
|
| 152 |
+
INFO: Created 14222 chunks
|
| 153 |
+
INFO: QA chain ready โ
|
| 154 |
+
INFO: ๐ฎ Server ready!
|
| 155 |
+
INFO: Uvicorn running on http://0.0.0.0:7860
|
| 156 |
+
```
|
| 157 |
+
|
| 158 |
+
4. The status indicator turns **green** โ your Space is live! ๐
|
| 159 |
+
|
| 160 |
+
---
|
| 161 |
+
|
| 162 |
+
## PHASE 7 โ Get Your Permanent URL & Test It
|
| 163 |
+
|
| 164 |
+
### 7.1 Your URL
|
| 165 |
+
```
|
| 166 |
+
https://YOUR_USERNAME-kumpas-rag.hf.space
|
| 167 |
+
```
|
| 168 |
+
Example: `https://jdelacruz-kumpas-rag.hf.space`
|
| 169 |
+
|
| 170 |
+
**This URL never changes.** Unlike ngrok, it's permanent.
|
| 171 |
+
|
| 172 |
+
### 7.2 Test in browser
|
| 173 |
+
```
|
| 174 |
+
https://jdelacruz-kumpas-rag.hf.space/health
|
| 175 |
+
```
|
| 176 |
+
Should return:
|
| 177 |
+
```json
|
| 178 |
+
{"status": "ok", "oracle": "ready", "model": "llama-3.3-70b-versatile"}
|
| 179 |
+
```
|
| 180 |
+
|
| 181 |
+
### 7.3 Test the /ask endpoint
|
| 182 |
+
Open: `https://jdelacruz-kumpas-rag.hf.space/docs`
|
| 183 |
+
|
| 184 |
+
This opens Swagger UI. Click **POST /ask** โ **Try it out** โ paste:
|
| 185 |
+
```json
|
| 186 |
+
{
|
| 187 |
+
"question": "Who is Voldemort?",
|
| 188 |
+
"history": []
|
| 189 |
+
}
|
| 190 |
+
```
|
| 191 |
+
Click **Execute** โ you should get a proper answer!
|
| 192 |
+
|
| 193 |
+
---
|
| 194 |
+
|
| 195 |
+
## PHASE 8 โ Update Your ASP.NET App
|
| 196 |
+
|
| 197 |
+
Open `appsettings.json` and update permanently:
|
| 198 |
+
|
| 199 |
+
```json
|
| 200 |
+
{
|
| 201 |
+
"Rag": {
|
| 202 |
+
"BaseUrl": "https://YOUR_USERNAME-kumpas-rag.hf.space",
|
| 203 |
+
"ApiKey": ""
|
| 204 |
+
}
|
| 205 |
+
}
|
| 206 |
+
```
|
| 207 |
+
|
| 208 |
+
Restart your ASP.NET app โ open the chatbot โ it now uses the always-on server! โ
|
| 209 |
+
|
| 210 |
+
---
|
| 211 |
+
|
| 212 |
+
## Important Notes About Free HF Spaces
|
| 213 |
+
|
| 214 |
+
| Thing | Detail |
|
| 215 |
+
|---|---|
|
| 216 |
+
| **Sleep after inactivity** | Free spaces sleep after ~15 min of no traffic. First request after sleep takes ~30 sec to wake up. |
|
| 217 |
+
| **CPU only** | Free tier is CPU โ FAISS and inference still work, just no GPU speedup. |
|
| 218 |
+
| **Storage** | 50GB limit โ your files are well under this. |
|
| 219 |
+
| **Build time** | ~5-8 min on first build. Rebuilds on every `git push`. |
|
| 220 |
+
| **Always-on** | Upgrade to HF Pro ($9/month) to disable sleep. |
|
| 221 |
+
|
| 222 |
+
### Handling the "sleep" wake-up delay
|
| 223 |
+
The first chatbot message after inactivity will take ~30 seconds. You can handle this gracefully in the chatbot widget โ in `chatbot.js`, the timeout is already set to 30 seconds in `RagService.cs`. The user will see the typing dots while it wakes up.
|
| 224 |
+
|
| 225 |
+
---
|
| 226 |
+
|
| 227 |
+
## Updating Your Code Later
|
| 228 |
+
|
| 229 |
+
Whenever you want to update the RAG server (e.g. change the prompt):
|
| 230 |
+
|
| 231 |
+
```bash
|
| 232 |
+
# Edit app.py locally, then:
|
| 233 |
+
git add app.py
|
| 234 |
+
git commit -m "update prompt"
|
| 235 |
+
git push
|
| 236 |
+
```
|
| 237 |
+
|
| 238 |
+
HF Spaces auto-rebuilds on every push โ takes ~3-5 minutes.
|
| 239 |
+
|
| 240 |
+
---
|
| 241 |
+
|
| 242 |
+
## Troubleshooting
|
| 243 |
+
|
| 244 |
+
### โ Build fails: "GROQ_API_KEY not set"
|
| 245 |
+
- You forgot Phase 3. Go to Space Settings โ Repository secrets โ add `GROQ_API_KEY`.
|
| 246 |
+
- Then go to **Factory reboot** in Space settings to trigger a fresh build.
|
| 247 |
+
|
| 248 |
+
### โ "harrypotter.txt not found"
|
| 249 |
+
- The file wasn't pushed. Check it's in your folder: `ls -la` should show it.
|
| 250 |
+
- Re-run `git add . && git commit -m "add text" && git push`
|
| 251 |
+
|
| 252 |
+
### โ Space shows "Runtime error"
|
| 253 |
+
- Click **Logs** tab โ read the error message.
|
| 254 |
+
- Most common cause: missing file or wrong env var name.
|
| 255 |
+
|
| 256 |
+
### โ Push rejected: file too large
|
| 257 |
+
- `harrypotter.txt` might be >10MB. HF needs Git LFS for that.
|
| 258 |
+
- Make sure you ran `git lfs track "*.txt"` before `git add`.
|
| 259 |
+
|
| 260 |
+
### โ Answers are slow (10-20 seconds)
|
| 261 |
+
- Normal for free CPU tier with the LLaMA 70B model via Groq API.
|
| 262 |
+
- Groq's own latency is ~2-3 seconds. The rest is FAISS retrieval + network.
|
| 263 |
+
- Perfectly acceptable for a chatbot.
|
| 264 |
+
|
| 265 |
+
---
|
| 266 |
+
|
| 267 |
+
## Summary: The 3 Files You Must Have
|
| 268 |
+
|
| 269 |
+
| File | Purpose |
|
| 270 |
+
|---|---|
|
| 271 |
+
| `app.py` | The FastAPI server (already written for you) |
|
| 272 |
+
| `Dockerfile` | Tells HF how to build the container |
|
| 273 |
+
| `requirements.txt` | Python dependencies |
|
| 274 |
+
| `harrypotter.txt` | Your HP corpus โ YOU must add this |
|
| 275 |
+
| `README.md` | Space description + metadata |
|
README.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: KUMPAS Harry Potter RAG Oracle
|
| 3 |
+
emoji: ๐ฎ
|
| 4 |
+
colorFrom: purple
|
| 5 |
+
colorTo: yellow
|
| 6 |
+
sdk: docker
|
| 7 |
+
pinned: false
|
| 8 |
+
app_port: 7860
|
| 9 |
+
---
|
| 10 |
+
|
| 11 |
+
# ๐ฎ KUMPAS โ Wizarding Oracle RAG API
|
| 12 |
+
|
| 13 |
+
A Harry Potter RAG (Retrieval-Augmented Generation) API built for the KUMPAS web app.
|
| 14 |
+
|
| 15 |
+
## Stack
|
| 16 |
+
- **Vector Store**: FAISS with `all-MiniLM-L6-v2` embeddings
|
| 17 |
+
- **LLM**: Groq `llama-3.3-70b-versatile`
|
| 18 |
+
- **Framework**: FastAPI
|
| 19 |
+
|
| 20 |
+
## Endpoints
|
| 21 |
+
|
| 22 |
+
| Method | Path | Description |
|
| 23 |
+
|--------|------|-------------|
|
| 24 |
+
| GET | `/health` | Server status |
|
| 25 |
+
| POST | `/ask` | Ask the Oracle |
|
| 26 |
+
|
| 27 |
+
## Request Format
|
| 28 |
+
|
| 29 |
+
```json
|
| 30 |
+
POST /ask
|
| 31 |
+
{
|
| 32 |
+
"question": "Who is Harry Potter's best friend?",
|
| 33 |
+
"history": [
|
| 34 |
+
{ "role": "user", "content": "previous question" },
|
| 35 |
+
{ "role": "assistant", "content": "previous answer" }
|
| 36 |
+
]
|
| 37 |
+
}
|
| 38 |
+
```
|
| 39 |
+
|
| 40 |
+
## Response Format
|
| 41 |
+
|
| 42 |
+
```json
|
| 43 |
+
{
|
| 44 |
+
"answer": "Harry Potter's best friend is Ron Weasley..."
|
| 45 |
+
}
|
| 46 |
+
```
|
app.py
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# app.py โ KUMPAS RAG Server for Hugging Face Spaces
|
| 2 |
+
# Stack: FAISS + HuggingFace all-MiniLM-L6-v2 + Groq llama-3.3-70b
|
| 3 |
+
|
| 4 |
+
import os
|
| 5 |
+
import logging
|
| 6 |
+
from contextlib import asynccontextmanager
|
| 7 |
+
from typing import List, Optional
|
| 8 |
+
|
| 9 |
+
from fastapi import FastAPI, HTTPException
|
| 10 |
+
from fastapi.middleware.cors import CORSMiddleware
|
| 11 |
+
from pydantic import BaseModel
|
| 12 |
+
|
| 13 |
+
from langchain_community.embeddings import HuggingFaceEmbeddings
|
| 14 |
+
from langchain_community.vectorstores import FAISS
|
| 15 |
+
from langchain_groq import ChatGroq
|
| 16 |
+
from langchain_core.prompts import ChatPromptTemplate
|
| 17 |
+
from langchain_core.runnables import RunnablePassthrough
|
| 18 |
+
from langchain_core.output_parsers import StrOutputParser
|
| 19 |
+
|
| 20 |
+
logging.basicConfig(level=logging.INFO)
|
| 21 |
+
logger = logging.getLogger(__name__)
|
| 22 |
+
|
| 23 |
+
# โโ Global chain (loaded once at startup) โโโโโโโโโโโโโโโโโโโโโโโ
|
| 24 |
+
qa_chain = None
|
| 25 |
+
|
| 26 |
+
def build_chain():
|
| 27 |
+
"""
|
| 28 |
+
Loads the FAISS index from disk and builds the QA chain.
|
| 29 |
+
Called once when the server starts.
|
| 30 |
+
"""
|
| 31 |
+
global qa_chain
|
| 32 |
+
|
| 33 |
+
groq_api_key = os.environ.get("GROQ_API_KEY", "")
|
| 34 |
+
if not groq_api_key:
|
| 35 |
+
raise RuntimeError("GROQ_API_KEY environment variable is not set!")
|
| 36 |
+
|
| 37 |
+
faiss_path = os.environ.get("FAISS_PATH", "student_manual_faiss")
|
| 38 |
+
text_path = os.environ.get("TEXT_PATH", "harrypotter.txt")
|
| 39 |
+
|
| 40 |
+
logger.info("Loading embeddings model...")
|
| 41 |
+
embeddings = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")
|
| 42 |
+
|
| 43 |
+
# โโ Try loading saved FAISS index first (fast) โโโโโโโโโโโโโโ
|
| 44 |
+
if os.path.exists(faiss_path):
|
| 45 |
+
logger.info(f"Loading FAISS index from {faiss_path}...")
|
| 46 |
+
vectorstore = FAISS.load_local(
|
| 47 |
+
faiss_path,
|
| 48 |
+
embeddings,
|
| 49 |
+
allow_dangerous_deserialization=True
|
| 50 |
+
)
|
| 51 |
+
logger.info("FAISS index loaded from disk โ
")
|
| 52 |
+
|
| 53 |
+
# โโ Otherwise build from text file (slow, first run only) โโโ
|
| 54 |
+
elif os.path.exists(text_path):
|
| 55 |
+
logger.info(f"Building FAISS index from {text_path} (this takes a few minutes)...")
|
| 56 |
+
from langchain_text_splitters import RecursiveCharacterTextSplitter
|
| 57 |
+
|
| 58 |
+
try:
|
| 59 |
+
with open(text_path, "r", encoding="latin-1") as f:
|
| 60 |
+
text = f.read()
|
| 61 |
+
except UnicodeDecodeError:
|
| 62 |
+
with open(text_path, "r", encoding="cp1252") as f:
|
| 63 |
+
text = f.read()
|
| 64 |
+
|
| 65 |
+
splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
|
| 66 |
+
chunks = splitter.split_text(text)
|
| 67 |
+
logger.info(f"Created {len(chunks)} chunks")
|
| 68 |
+
|
| 69 |
+
vectorstore = FAISS.from_texts(chunks, embedding=embeddings)
|
| 70 |
+
vectorstore.save_local(faiss_path)
|
| 71 |
+
logger.info("FAISS index built and saved โ
")
|
| 72 |
+
|
| 73 |
+
else:
|
| 74 |
+
raise RuntimeError(
|
| 75 |
+
f"Neither FAISS index ({faiss_path}) nor text file ({text_path}) found! "
|
| 76 |
+
"Make sure harrypotter.txt is in your Space files."
|
| 77 |
+
)
|
| 78 |
+
|
| 79 |
+
# โโ Build retriever โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 80 |
+
retriever = vectorstore.as_retriever(
|
| 81 |
+
search_type="similarity",
|
| 82 |
+
search_kwargs={"k": 3}
|
| 83 |
+
)
|
| 84 |
+
|
| 85 |
+
# โโ Groq LLM โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 86 |
+
llm = ChatGroq(
|
| 87 |
+
model="llama-3.3-70b-versatile",
|
| 88 |
+
temperature=0.5,
|
| 89 |
+
groq_api_key=groq_api_key
|
| 90 |
+
)
|
| 91 |
+
|
| 92 |
+
# โโ Prompt (same as your Colab notebook) โโโโโโโโโโโโโโโโโโโโ
|
| 93 |
+
prompt = ChatPromptTemplate.from_template("""
|
| 94 |
+
Answer the question based only on the following context from the Harry Potter series:
|
| 95 |
+
{context}
|
| 96 |
+
|
| 97 |
+
Question: {question}
|
| 98 |
+
|
| 99 |
+
Answer clearly and concisely. If the answer is not in the context, say so honestly.
|
| 100 |
+
""")
|
| 101 |
+
|
| 102 |
+
# โโ LCEL chain (identical to your Colab) โโโโโโโโโโโโโโโโโโโโ
|
| 103 |
+
qa_chain = (
|
| 104 |
+
{"context": retriever, "question": RunnablePassthrough()}
|
| 105 |
+
| prompt
|
| 106 |
+
| llm
|
| 107 |
+
| StrOutputParser()
|
| 108 |
+
)
|
| 109 |
+
|
| 110 |
+
logger.info("QA chain ready โ
")
|
| 111 |
+
return qa_chain
|
| 112 |
+
|
| 113 |
+
|
| 114 |
+
# โโ Lifespan: build chain on startup โโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 115 |
+
@asynccontextmanager
|
| 116 |
+
async def lifespan(app: FastAPI):
|
| 117 |
+
logger.info("๐ฎ Starting KUMPAS RAG server...")
|
| 118 |
+
build_chain()
|
| 119 |
+
logger.info("๐ฎ Server ready!")
|
| 120 |
+
yield
|
| 121 |
+
logger.info("Server shutting down.")
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
# โโ FastAPI app โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 125 |
+
app = FastAPI(
|
| 126 |
+
title="KUMPAS Harry Potter RAG API",
|
| 127 |
+
description="Wizarding Oracle โ FAISS + Groq LLaMA",
|
| 128 |
+
version="1.0.0",
|
| 129 |
+
lifespan=lifespan
|
| 130 |
+
)
|
| 131 |
+
|
| 132 |
+
app.add_middleware(
|
| 133 |
+
CORSMiddleware,
|
| 134 |
+
allow_origins=["*"],
|
| 135 |
+
allow_methods=["GET", "POST"],
|
| 136 |
+
allow_headers=["*"],
|
| 137 |
+
)
|
| 138 |
+
|
| 139 |
+
|
| 140 |
+
# โโ Request / Response models โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 141 |
+
class HistoryItem(BaseModel):
|
| 142 |
+
role: str
|
| 143 |
+
content: str
|
| 144 |
+
|
| 145 |
+
class AskRequest(BaseModel):
|
| 146 |
+
question: str
|
| 147 |
+
history: Optional[List[HistoryItem]] = []
|
| 148 |
+
|
| 149 |
+
class AskResponse(BaseModel):
|
| 150 |
+
answer: str
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
# โโ Endpoints โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 154 |
+
@app.get("/")
|
| 155 |
+
def root():
|
| 156 |
+
return {"message": "๐ฎ KUMPAS Wizarding Oracle is alive!", "status": "ok"}
|
| 157 |
+
|
| 158 |
+
@app.get("/health")
|
| 159 |
+
def health():
|
| 160 |
+
return {
|
| 161 |
+
"status": "ok",
|
| 162 |
+
"oracle": "ready" if qa_chain else "loading",
|
| 163 |
+
"model": "llama-3.3-70b-versatile"
|
| 164 |
+
}
|
| 165 |
+
|
| 166 |
+
@app.post("/ask", response_model=AskResponse)
|
| 167 |
+
def ask(req: AskRequest):
|
| 168 |
+
if qa_chain is None:
|
| 169 |
+
raise HTTPException(status_code=503, detail="Oracle is still initializing. Try again in a moment.")
|
| 170 |
+
|
| 171 |
+
question = req.question.strip()
|
| 172 |
+
if not question:
|
| 173 |
+
raise HTTPException(status_code=400, detail="Question cannot be empty.")
|
| 174 |
+
|
| 175 |
+
# โโ Prepend recent history to give the LLM context โโโโโโโโโโ
|
| 176 |
+
if req.history:
|
| 177 |
+
recent = req.history[-4:] # last 2 turns
|
| 178 |
+
history_text = "\n".join(
|
| 179 |
+
f"{'User' if h.role == 'user' else 'Oracle'}: {h.content}"
|
| 180 |
+
for h in recent
|
| 181 |
+
)
|
| 182 |
+
full_question = f"Previous conversation:\n{history_text}\n\nCurrent question: {question}"
|
| 183 |
+
else:
|
| 184 |
+
full_question = question
|
| 185 |
+
|
| 186 |
+
try:
|
| 187 |
+
logger.info(f"Question: {question[:80]}...")
|
| 188 |
+
answer = qa_chain.invoke(full_question)
|
| 189 |
+
answer = str(answer).strip()
|
| 190 |
+
|
| 191 |
+
if not answer:
|
| 192 |
+
answer = "The wizarding archives hold no record of this. Try rephrasing your question."
|
| 193 |
+
|
| 194 |
+
logger.info(f"Answer: {answer[:80]}...")
|
| 195 |
+
return AskResponse(answer=answer)
|
| 196 |
+
|
| 197 |
+
except Exception as e:
|
| 198 |
+
logger.error(f"Chain error: {e}")
|
| 199 |
+
raise HTTPException(
|
| 200 |
+
status_code=500,
|
| 201 |
+
detail="The magic is disrupted. The Oracle cannot answer right now."
|
| 202 |
+
)
|
harrypotter.txt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:565fb203ee98c0002c14de4df4dd1b648254f4d5d64d439574ddd62ca235b3b0
|
| 3 |
+
size 6617234
|
requirements.txt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:696c6e9ed85fa80cdb8240845987008f6a18ca05a7d65c534de9a70f73526b04
|
| 3 |
+
size 285
|