daniel-simeone commited on
Commit ·
a71ea0a
1
Parent(s): 4bce094
initial app
Browse files- DEPLOYMENT.md +214 -0
- README.md +189 -6
- SETUP_PATH.md +70 -0
- app.py +295 -0
- example_usage.py +53 -0
- ingest_documents.py +87 -0
- ingestion.py +231 -0
- pdfs/.gitkeep +2 -0
- requirements.txt +12 -0
DEPLOYMENT.md
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Deploying to Hugging Face Spaces
|
| 2 |
+
|
| 3 |
+
This guide will walk you through deploying your RAG chatbot to Hugging Face Spaces.
|
| 4 |
+
|
| 5 |
+
## Prerequisites
|
| 6 |
+
|
| 7 |
+
1. **Hugging Face Account**: Sign up at https://huggingface.co/join
|
| 8 |
+
2. **Access Token**: Get your token from https://huggingface.co/settings/tokens
|
| 9 |
+
|
| 10 |
+
## Step-by-Step Deployment
|
| 11 |
+
|
| 12 |
+
### Method 1: Using the Web Interface (Easiest)
|
| 13 |
+
|
| 14 |
+
1. **Create a New Space**:
|
| 15 |
+
- Go to https://huggingface.co/new-space
|
| 16 |
+
- Fill in:
|
| 17 |
+
- **Space name**: Choose a name (e.g., `my-rag-chatbot`)
|
| 18 |
+
- **SDK**: Select **Gradio**
|
| 19 |
+
- **Hardware**: Choose based on your needs:
|
| 20 |
+
- **CPU basic**: Free, good for testing
|
| 21 |
+
- **CPU upgrade**: Better performance
|
| 22 |
+
- **GPU**: If you need faster model inference
|
| 23 |
+
- **Visibility**: Public or Private
|
| 24 |
+
- Click **Create Space**
|
| 25 |
+
|
| 26 |
+
2. **Upload Your Files**:
|
| 27 |
+
- In your new Space, click the **Files and versions** tab
|
| 28 |
+
- Click **Add file** → **Upload files**
|
| 29 |
+
- Upload these files:
|
| 30 |
+
- `app.py`
|
| 31 |
+
- `ingestion.py`
|
| 32 |
+
- `requirements.txt`
|
| 33 |
+
- `README.md` (optional but recommended)
|
| 34 |
+
- `pdfs/` folder (if you want to include sample PDFs)
|
| 35 |
+
- `ingest_documents.py` (optional, for manual ingestion)
|
| 36 |
+
|
| 37 |
+
3. **Important Notes**:
|
| 38 |
+
- **Vector Store**: The `vector_store/` folder is in `.gitignore` and won't be uploaded. You have two options:
|
| 39 |
+
- **Option A**: Run `ingest_documents.py` on the Space after deployment (via the Space's terminal)
|
| 40 |
+
- **Option B**: Upload the vector store files manually if they're not too large
|
| 41 |
+
- **PDFs**: If your PDFs are large (>50MB), consider hosting them elsewhere or using Hugging Face Datasets
|
| 42 |
+
|
| 43 |
+
4. **Wait for Build**: Hugging Face will automatically:
|
| 44 |
+
- Install dependencies from `requirements.txt`
|
| 45 |
+
- Start your Gradio app
|
| 46 |
+
- Your Space will be live at: `https://huggingface.co/spaces/YOUR_USERNAME/YOUR_SPACE_NAME`
|
| 47 |
+
|
| 48 |
+
### Method 2: Using Git (Recommended for Updates)
|
| 49 |
+
|
| 50 |
+
1. **Install Git** (if not already installed):
|
| 51 |
+
- Windows: Download from https://git-scm.com/download/win
|
| 52 |
+
- Or use Git that comes with GitHub Desktop
|
| 53 |
+
|
| 54 |
+
2. **Install Hugging Face CLI**:
|
| 55 |
+
```bash
|
| 56 |
+
py -m pip install huggingface_hub
|
| 57 |
+
```
|
| 58 |
+
|
| 59 |
+
3. **Login to Hugging Face**:
|
| 60 |
+
```bash
|
| 61 |
+
huggingface-cli login
|
| 62 |
+
```
|
| 63 |
+
Enter your access token when prompted.
|
| 64 |
+
|
| 65 |
+
4. **Create a New Space** (via web interface):
|
| 66 |
+
- Go to https://huggingface.co/new-space
|
| 67 |
+
- Create the space with Gradio SDK
|
| 68 |
+
- Note your space name (e.g., `YOUR_USERNAME/my-rag-chatbot`)
|
| 69 |
+
|
| 70 |
+
5. **Initialize Git in Your Project**:
|
| 71 |
+
```bash
|
| 72 |
+
cd C:\Users\DanielSimeone\Desktop\testing-hugging-face
|
| 73 |
+
git init
|
| 74 |
+
git add app.py ingestion.py requirements.txt README.md ingest_documents.py pdfs/
|
| 75 |
+
git commit -m "Initial commit for Hugging Face Space"
|
| 76 |
+
```
|
| 77 |
+
|
| 78 |
+
6. **Add Hugging Face Remote and Push**:
|
| 79 |
+
```bash
|
| 80 |
+
git remote add origin https://huggingface.co/spaces/YOUR_USERNAME/YOUR_SPACE_NAME
|
| 81 |
+
git push -u origin main
|
| 82 |
+
```
|
| 83 |
+
|
| 84 |
+
7. **For Future Updates**:
|
| 85 |
+
```bash
|
| 86 |
+
git add .
|
| 87 |
+
git commit -m "Update description"
|
| 88 |
+
git push
|
| 89 |
+
```
|
| 90 |
+
|
| 91 |
+
## Post-Deployment Setup
|
| 92 |
+
|
| 93 |
+
### Setting Up the Vector Store on Hugging Face Spaces
|
| 94 |
+
|
| 95 |
+
Since the vector store isn't included in the repository, you need to create it on the Space:
|
| 96 |
+
|
| 97 |
+
1. **Option A: Use the Space Terminal** (if available):
|
| 98 |
+
- Go to your Space → **Settings** → Enable **Embedded Gradio SDK**
|
| 99 |
+
- Or use the Space's built-in terminal/console
|
| 100 |
+
- Run: `python ingest_documents.py`
|
| 101 |
+
|
| 102 |
+
2. **Option B: Upload Vector Store Files**:
|
| 103 |
+
- If your vector store files are small enough:
|
| 104 |
+
- Upload `vector_store/index.faiss`
|
| 105 |
+
- Upload `vector_store/documents.pkl`
|
| 106 |
+
- Upload `vector_store/embeddings.pkl`
|
| 107 |
+
- The app will automatically load them on startup
|
| 108 |
+
|
| 109 |
+
3. **Option C: Pre-build in a Script**:
|
| 110 |
+
- Create a `setup.py` or modify the Space to run ingestion on first launch
|
| 111 |
+
- This is more complex but ensures the vector store is always ready
|
| 112 |
+
|
| 113 |
+
## Required Files for Deployment
|
| 114 |
+
|
| 115 |
+
Your Space needs these files:
|
| 116 |
+
|
| 117 |
+
- ✅ `app.py` - Main Gradio application
|
| 118 |
+
- ✅ `ingestion.py` - Document ingestion module
|
| 119 |
+
- ✅ `requirements.txt` - Python dependencies
|
| 120 |
+
- ✅ `README.md` - Documentation (optional but recommended)
|
| 121 |
+
- ⚠️ `vector_store/` - Will be created on the Space
|
| 122 |
+
- ⚠️ `pdfs/` - Optional, include if you want sample PDFs
|
| 123 |
+
|
| 124 |
+
## Configuration for Hugging Face Spaces
|
| 125 |
+
|
| 126 |
+
### Update app.py for Spaces
|
| 127 |
+
|
| 128 |
+
The current `app.py` should work, but you might want to adjust:
|
| 129 |
+
|
| 130 |
+
1. **Port**: Hugging Face Spaces uses port 7860 automatically
|
| 131 |
+
2. **Server name**: Use `0.0.0.0` (already set)
|
| 132 |
+
3. **Share**: Set to `False` (already set)
|
| 133 |
+
|
| 134 |
+
Your current launch code is fine:
|
| 135 |
+
```python
|
| 136 |
+
app.launch(
|
| 137 |
+
share=False,
|
| 138 |
+
server_name="0.0.0.0",
|
| 139 |
+
server_port=7861, # Note: Spaces uses 7860, but this should auto-adjust
|
| 140 |
+
theme=MinimalistTheme()
|
| 141 |
+
)
|
| 142 |
+
```
|
| 143 |
+
|
| 144 |
+
### Optional: Add README Frontmatter
|
| 145 |
+
|
| 146 |
+
Add this to the top of your `README.md` for better Space display:
|
| 147 |
+
|
| 148 |
+
```yaml
|
| 149 |
+
---
|
| 150 |
+
title: RAG Chatbot
|
| 151 |
+
emoji: 🤖
|
| 152 |
+
colorFrom: blue
|
| 153 |
+
colorTo: purple
|
| 154 |
+
sdk: gradio
|
| 155 |
+
sdk_version: 6.3.0
|
| 156 |
+
app_file: app.py
|
| 157 |
+
pinned: false
|
| 158 |
+
---
|
| 159 |
+
```
|
| 160 |
+
|
| 161 |
+
## Troubleshooting
|
| 162 |
+
|
| 163 |
+
### Build Fails
|
| 164 |
+
|
| 165 |
+
- Check that all dependencies in `requirements.txt` are correct
|
| 166 |
+
- Ensure Python version compatibility (Spaces uses Python 3.10 by default)
|
| 167 |
+
- Check the build logs in your Space's **Logs** tab
|
| 168 |
+
|
| 169 |
+
### Vector Store Not Loading
|
| 170 |
+
|
| 171 |
+
- Verify the vector store files are in the correct location
|
| 172 |
+
- Check file permissions
|
| 173 |
+
- Ensure the path in `app.py` is correct (should be `./vector_store`)
|
| 174 |
+
|
| 175 |
+
### Model Loading Issues
|
| 176 |
+
|
| 177 |
+
- Large models may take time to download on first run
|
| 178 |
+
- Consider using smaller models for faster startup
|
| 179 |
+
- Check available disk space in your Space
|
| 180 |
+
|
| 181 |
+
### Memory Issues
|
| 182 |
+
|
| 183 |
+
- If you get out-of-memory errors, consider:
|
| 184 |
+
- Using a smaller embedding model
|
| 185 |
+
- Reducing chunk size in `ingestion.py`
|
| 186 |
+
- Upgrading to a Space with more memory
|
| 187 |
+
|
| 188 |
+
## Updating Your Space
|
| 189 |
+
|
| 190 |
+
After making changes locally:
|
| 191 |
+
|
| 192 |
+
```bash
|
| 193 |
+
git add .
|
| 194 |
+
git commit -m "Description of changes"
|
| 195 |
+
git push
|
| 196 |
+
```
|
| 197 |
+
|
| 198 |
+
Hugging Face will automatically rebuild your Space.
|
| 199 |
+
|
| 200 |
+
## Sharing Your Space
|
| 201 |
+
|
| 202 |
+
Once deployed, your Space will be available at:
|
| 203 |
+
```
|
| 204 |
+
https://huggingface.co/spaces/YOUR_USERNAME/YOUR_SPACE_NAME
|
| 205 |
+
```
|
| 206 |
+
|
| 207 |
+
You can share this URL with others!
|
| 208 |
+
|
| 209 |
+
## Next Steps
|
| 210 |
+
|
| 211 |
+
- Add more PDFs to the `pdfs/` folder
|
| 212 |
+
- Update URLs in `ingest_documents.py`
|
| 213 |
+
- Customize the theme further
|
| 214 |
+
- Add more features to the chatbot
|
README.md
CHANGED
|
@@ -1,13 +1,196 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
-
colorTo:
|
| 6 |
sdk: gradio
|
| 7 |
sdk_version: 6.3.0
|
| 8 |
app_file: app.py
|
| 9 |
pinned: false
|
| 10 |
-
short_description: testing
|
| 11 |
---
|
| 12 |
|
| 13 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: RAG Chatbot
|
| 3 |
+
emoji: 🤖
|
| 4 |
+
colorFrom: blue
|
| 5 |
+
colorTo: purple
|
| 6 |
sdk: gradio
|
| 7 |
sdk_version: 6.3.0
|
| 8 |
app_file: app.py
|
| 9 |
pinned: false
|
|
|
|
| 10 |
---
|
| 11 |
|
| 12 |
+
# Hugging Face RAG Chatbot
|
| 13 |
+
|
| 14 |
+
A Retrieval-Augmented Generation (RAG) chatbot that can ingest PDFs and URLs, then answer questions based on the ingested documents. Built for deployment on Hugging Face Spaces.
|
| 15 |
+
|
| 16 |
+
## Features
|
| 17 |
+
|
| 18 |
+
- 📄 **PDF Ingestion**: Upload and process PDF documents
|
| 19 |
+
- 🌐 **URL Ingestion**: Extract and process content from web URLs
|
| 20 |
+
- 🔍 **Vector Search**: Semantic search using sentence transformers
|
| 21 |
+
- 💬 **Chatbot Interface**: Interactive Gradio interface for querying documents
|
| 22 |
+
- 🚀 **Hugging Face Ready**: Configured for easy deployment to Hugging Face Spaces
|
| 23 |
+
|
| 24 |
+
## Setup
|
| 25 |
+
|
| 26 |
+
### Local Development
|
| 27 |
+
|
| 28 |
+
1. **Install dependencies:**
|
| 29 |
+
```bash
|
| 30 |
+
pip install -r requirements.txt
|
| 31 |
+
```
|
| 32 |
+
|
| 33 |
+
2. **Run the application:**
|
| 34 |
+
```bash
|
| 35 |
+
python app.py
|
| 36 |
+
```
|
| 37 |
+
|
| 38 |
+
3. **Access the interface:**
|
| 39 |
+
- Open your browser to `http://localhost:7860`
|
| 40 |
+
|
| 41 |
+
### Usage
|
| 42 |
+
|
| 43 |
+
1. **Ingest Documents (Run this first or periodically to update):**
|
| 44 |
+
- Add PDF files to the `pdfs/` folder
|
| 45 |
+
- Edit `ingest_documents.py` and add your URLs to the `URLS` list
|
| 46 |
+
- Run the ingestion script:
|
| 47 |
+
```bash
|
| 48 |
+
py ingest_documents.py
|
| 49 |
+
```
|
| 50 |
+
- Wait for processing to complete (this creates/updates the vector store)
|
| 51 |
+
|
| 52 |
+
2. **Chat with Documents:**
|
| 53 |
+
- Run the chatbot app:
|
| 54 |
+
```bash
|
| 55 |
+
py app.py
|
| 56 |
+
```
|
| 57 |
+
- Open your browser to `http://localhost:7860`
|
| 58 |
+
- Toggle "Use RAG" to enable/disable document retrieval
|
| 59 |
+
- Ask questions about your ingested documents
|
| 60 |
+
- The chatbot will retrieve relevant context and generate answers
|
| 61 |
+
|
| 62 |
+
## Deployment to Hugging Face Spaces
|
| 63 |
+
|
| 64 |
+
### Option 1: Using Hugging Face CLI
|
| 65 |
+
|
| 66 |
+
1. **Install Hugging Face CLI:**
|
| 67 |
+
```bash
|
| 68 |
+
pip install huggingface_hub
|
| 69 |
+
```
|
| 70 |
+
|
| 71 |
+
2. **Login to Hugging Face:**
|
| 72 |
+
```bash
|
| 73 |
+
huggingface-cli login
|
| 74 |
+
```
|
| 75 |
+
|
| 76 |
+
3. **Create a new Space:**
|
| 77 |
+
- Go to https://huggingface.co/new-space
|
| 78 |
+
- Choose a name and select "Gradio" as the SDK
|
| 79 |
+
- Create the space
|
| 80 |
+
|
| 81 |
+
4. **Clone and push your code:**
|
| 82 |
+
```bash
|
| 83 |
+
git clone https://huggingface.co/spaces/YOUR_USERNAME/YOUR_SPACE_NAME
|
| 84 |
+
cd YOUR_SPACE_NAME
|
| 85 |
+
# Copy your files here
|
| 86 |
+
git add .
|
| 87 |
+
git commit -m "Initial commit"
|
| 88 |
+
git push
|
| 89 |
+
```
|
| 90 |
+
|
| 91 |
+
### Option 2: Using Git
|
| 92 |
+
|
| 93 |
+
1. **Initialize git repository:**
|
| 94 |
+
```bash
|
| 95 |
+
git init
|
| 96 |
+
git add .
|
| 97 |
+
git commit -m "Initial commit"
|
| 98 |
+
```
|
| 99 |
+
|
| 100 |
+
2. **Add Hugging Face remote:**
|
| 101 |
+
```bash
|
| 102 |
+
git remote add origin https://huggingface.co/spaces/YOUR_USERNAME/YOUR_SPACE_NAME
|
| 103 |
+
git push -u origin main
|
| 104 |
+
```
|
| 105 |
+
|
| 106 |
+
### Required Files for Hugging Face Spaces
|
| 107 |
+
|
| 108 |
+
Your Space needs these files:
|
| 109 |
+
- `app.py` - Main Gradio application
|
| 110 |
+
- `requirements.txt` - Python dependencies
|
| 111 |
+
- `README.md` - This file (optional but recommended)
|
| 112 |
+
|
| 113 |
+
### Optional: Add app.py to README
|
| 114 |
+
|
| 115 |
+
For Hugging Face Spaces, you can also add a `app.py` reference in your README:
|
| 116 |
+
|
| 117 |
+
```yaml
|
| 118 |
+
---
|
| 119 |
+
title: RAG Chatbot
|
| 120 |
+
emoji: 🤖
|
| 121 |
+
colorFrom: blue
|
| 122 |
+
colorTo: purple
|
| 123 |
+
sdk: gradio
|
| 124 |
+
sdk_version: 4.0.0
|
| 125 |
+
app_file: app.py
|
| 126 |
+
pinned: false
|
| 127 |
+
---
|
| 128 |
+
```
|
| 129 |
+
|
| 130 |
+
## Configuration
|
| 131 |
+
|
| 132 |
+
### Changing the Chatbot Model
|
| 133 |
+
|
| 134 |
+
Edit `app.py` and change the `model_name` parameter in `RAGChatbot`:
|
| 135 |
+
|
| 136 |
+
```python
|
| 137 |
+
chatbot = RAGChatbot(model_name="your-preferred-model")
|
| 138 |
+
```
|
| 139 |
+
|
| 140 |
+
Popular options:
|
| 141 |
+
- `microsoft/DialoGPT-medium` (default)
|
| 142 |
+
- `gpt2`
|
| 143 |
+
- `facebook/blenderbot-400M-distill`
|
| 144 |
+
- `microsoft/DialoGPT-large`
|
| 145 |
+
|
| 146 |
+
### Changing the Embedding Model
|
| 147 |
+
|
| 148 |
+
Edit the `embedding_model` parameter:
|
| 149 |
+
|
| 150 |
+
```python
|
| 151 |
+
chatbot = RAGChatbot(embedding_model="sentence-transformers/all-mpnet-base-v2")
|
| 152 |
+
```
|
| 153 |
+
|
| 154 |
+
## Project Structure
|
| 155 |
+
|
| 156 |
+
```
|
| 157 |
+
.
|
| 158 |
+
├── app.py # Main Gradio chatbot application
|
| 159 |
+
├── ingest_documents.py # Standalone script to ingest PDFs and URLs
|
| 160 |
+
├── ingestion.py # Document ingestion and vector store module
|
| 161 |
+
├── requirements.txt # Python dependencies
|
| 162 |
+
├── README.md # This file
|
| 163 |
+
├── pdfs/ # Folder for PDF files (add your PDFs here)
|
| 164 |
+
│ └── README.md
|
| 165 |
+
└── vector_store/ # Saved vector store (created after ingestion)
|
| 166 |
+
├── index.faiss
|
| 167 |
+
├── documents.pkl
|
| 168 |
+
└── embeddings.pkl
|
| 169 |
+
```
|
| 170 |
+
|
| 171 |
+
## Limitations
|
| 172 |
+
|
| 173 |
+
- Vector store is stored locally (not persistent on Hugging Face Spaces by default)
|
| 174 |
+
- Large documents may take time to process
|
| 175 |
+
- Some URLs may be blocked or require authentication
|
| 176 |
+
- GPU recommended for better performance with larger models
|
| 177 |
+
|
| 178 |
+
## Troubleshooting
|
| 179 |
+
|
| 180 |
+
### Out of Memory Errors
|
| 181 |
+
- Use smaller models
|
| 182 |
+
- Reduce chunk size in `ingestion.py`
|
| 183 |
+
- Process fewer documents at once
|
| 184 |
+
|
| 185 |
+
### URL Fetching Issues
|
| 186 |
+
- Some websites block automated requests
|
| 187 |
+
- Try different URLs or use PDF uploads instead
|
| 188 |
+
|
| 189 |
+
### Model Loading Issues
|
| 190 |
+
- Ensure you have sufficient disk space
|
| 191 |
+
- Check your internet connection for model downloads
|
| 192 |
+
- Some models require GPU - check model requirements
|
| 193 |
+
|
| 194 |
+
## License
|
| 195 |
+
|
| 196 |
+
This project is open source and available under the MIT License.
|
SETUP_PATH.md
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Fixing Python PATH on Windows
|
| 2 |
+
|
| 3 |
+
Your Python 3.12 is installed but not in your PATH. Here's how to fix it:
|
| 4 |
+
|
| 5 |
+
## Quick Fix (Temporary - Current Session Only)
|
| 6 |
+
|
| 7 |
+
For now, you can use these commands:
|
| 8 |
+
- `py -m pip` instead of `pip`
|
| 9 |
+
- `py` instead of `python`
|
| 10 |
+
- `C:\Users\DanielSimeone\AppData\Local\Programs\Python\Python312\python.exe` for direct access
|
| 11 |
+
|
| 12 |
+
## Permanent Fix - Add Python to PATH
|
| 13 |
+
|
| 14 |
+
### Method 1: Using Windows Settings (Recommended)
|
| 15 |
+
|
| 16 |
+
1. **Open System Environment Variables:**
|
| 17 |
+
- Press `Win + X` and select "System"
|
| 18 |
+
- Click "Advanced system settings"
|
| 19 |
+
- Click "Environment Variables" button
|
| 20 |
+
|
| 21 |
+
2. **Edit PATH:**
|
| 22 |
+
- Under "User variables" (or "System variables" if you want it for all users), find and select "Path"
|
| 23 |
+
- Click "Edit"
|
| 24 |
+
- Click "New" and add these two paths:
|
| 25 |
+
```
|
| 26 |
+
C:\Users\DanielSimeone\AppData\Local\Programs\Python\Python312
|
| 27 |
+
C:\Users\DanielSimeone\AppData\Local\Programs\Python\Python312\Scripts
|
| 28 |
+
```
|
| 29 |
+
- Click "OK" on all dialogs
|
| 30 |
+
|
| 31 |
+
3. **Restart your terminal/PowerShell** for changes to take effect
|
| 32 |
+
|
| 33 |
+
### Method 2: Using PowerShell (Run as Administrator)
|
| 34 |
+
|
| 35 |
+
```powershell
|
| 36 |
+
[Environment]::SetEnvironmentVariable(
|
| 37 |
+
"Path",
|
| 38 |
+
[Environment]::GetEnvironmentVariable("Path", "User") + ";C:\Users\DanielSimeone\AppData\Local\Programs\Python\Python312;C:\Users\DanielSimeone\AppData\Local\Programs\Python\Python312\Scripts",
|
| 39 |
+
"User"
|
| 40 |
+
)
|
| 41 |
+
```
|
| 42 |
+
|
| 43 |
+
Then restart your terminal.
|
| 44 |
+
|
| 45 |
+
### Method 3: Reinstall Python with "Add to PATH" option
|
| 46 |
+
|
| 47 |
+
If you reinstall Python, make sure to check "Add Python to PATH" during installation.
|
| 48 |
+
|
| 49 |
+
## Verify It Works
|
| 50 |
+
|
| 51 |
+
After adding to PATH and restarting your terminal, test:
|
| 52 |
+
|
| 53 |
+
```bash
|
| 54 |
+
python --version
|
| 55 |
+
pip --version
|
| 56 |
+
```
|
| 57 |
+
|
| 58 |
+
Both should work without errors.
|
| 59 |
+
|
| 60 |
+
## For This Project
|
| 61 |
+
|
| 62 |
+
You can run the app using:
|
| 63 |
+
```bash
|
| 64 |
+
py app.py
|
| 65 |
+
```
|
| 66 |
+
|
| 67 |
+
Or after fixing PATH:
|
| 68 |
+
```bash
|
| 69 |
+
python app.py
|
| 70 |
+
```
|
app.py
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Gradio app for Hugging Face chatbot with RAG capabilities.
|
| 3 |
+
"""
|
| 4 |
+
import gradio as gr
|
| 5 |
+
from gradio.themes.base import Base
|
| 6 |
+
from gradio.themes.utils import colors, fonts, sizes
|
| 7 |
+
import os
|
| 8 |
+
from typing import List, Tuple
|
| 9 |
+
from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline
|
| 10 |
+
from ingestion import DocumentIngestion
|
| 11 |
+
import torch
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
# Create a clean minimalist theme
|
| 15 |
+
class MinimalistTheme(Base):
|
| 16 |
+
"""A clean, minimalist theme with subtle colors and simple styling."""
|
| 17 |
+
def __init__(self):
|
| 18 |
+
super().__init__(
|
| 19 |
+
primary_hue=colors.blue,
|
| 20 |
+
secondary_hue=colors.gray,
|
| 21 |
+
neutral_hue=colors.gray,
|
| 22 |
+
spacing_size=sizes.spacing_md,
|
| 23 |
+
radius_size=sizes.radius_sm,
|
| 24 |
+
text_size=sizes.text_md,
|
| 25 |
+
font=(
|
| 26 |
+
fonts.GoogleFont("Inter"),
|
| 27 |
+
"ui-sans-serif",
|
| 28 |
+
"system-ui",
|
| 29 |
+
"sans-serif",
|
| 30 |
+
),
|
| 31 |
+
font_mono=(
|
| 32 |
+
fonts.GoogleFont("JetBrains Mono"),
|
| 33 |
+
"ui-monospace",
|
| 34 |
+
"monospace",
|
| 35 |
+
),
|
| 36 |
+
)
|
| 37 |
+
super().set(
|
| 38 |
+
# Clean backgrounds
|
| 39 |
+
body_background_fill="#ffffff",
|
| 40 |
+
body_background_fill_dark="#0f0f0f",
|
| 41 |
+
block_background_fill="#ffffff",
|
| 42 |
+
block_background_fill_dark="#1a1a1a",
|
| 43 |
+
|
| 44 |
+
# Subtle borders
|
| 45 |
+
block_border_width="1px",
|
| 46 |
+
block_border_color="#e0e0e0",
|
| 47 |
+
block_border_color_dark="#2a2a2a",
|
| 48 |
+
block_shadow="none",
|
| 49 |
+
|
| 50 |
+
# Clean buttons
|
| 51 |
+
button_primary_background_fill="#2563eb",
|
| 52 |
+
button_primary_background_fill_hover="#1d4ed8",
|
| 53 |
+
button_primary_text_color="#ffffff",
|
| 54 |
+
button_primary_background_fill_dark="#3b82f6",
|
| 55 |
+
button_primary_background_fill_hover_dark="#2563eb",
|
| 56 |
+
button_secondary_background_fill="#f3f4f6",
|
| 57 |
+
button_secondary_background_fill_hover="#e5e7eb",
|
| 58 |
+
button_secondary_text_color="#111827",
|
| 59 |
+
button_secondary_background_fill_dark="#374151",
|
| 60 |
+
button_secondary_background_fill_hover_dark="#4b5563",
|
| 61 |
+
button_border_width="1px",
|
| 62 |
+
|
| 63 |
+
# Input fields
|
| 64 |
+
input_background_fill="#ffffff",
|
| 65 |
+
input_background_fill_dark="#1a1a1a",
|
| 66 |
+
input_border_width="1px",
|
| 67 |
+
input_border_color="#d1d5db",
|
| 68 |
+
input_border_color_dark="#374151",
|
| 69 |
+
|
| 70 |
+
# Text colors
|
| 71 |
+
body_text_color="#111827",
|
| 72 |
+
body_text_color_dark="#e5e7eb",
|
| 73 |
+
block_label_text_color="#374151",
|
| 74 |
+
block_label_text_color_dark="#9ca3af",
|
| 75 |
+
)
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
class RAGChatbot:
|
| 79 |
+
"""Chatbot with RAG capabilities."""
|
| 80 |
+
|
| 81 |
+
def __init__(
|
| 82 |
+
self,
|
| 83 |
+
model_name: str = "microsoft/DialoGPT-medium",
|
| 84 |
+
embedding_model: str = "all-MiniLM-L6-v2",
|
| 85 |
+
vector_store_path: str = "./vector_store"
|
| 86 |
+
):
|
| 87 |
+
"""
|
| 88 |
+
Initialize the RAG chatbot.
|
| 89 |
+
|
| 90 |
+
Args:
|
| 91 |
+
model_name: Hugging Face model name for the chatbot
|
| 92 |
+
embedding_model: Model for document embeddings
|
| 93 |
+
vector_store_path: Path to saved vector store
|
| 94 |
+
"""
|
| 95 |
+
self.model_name = model_name
|
| 96 |
+
self.device = "cuda" if torch.cuda.is_available() else "cpu"
|
| 97 |
+
|
| 98 |
+
# Load chatbot model
|
| 99 |
+
print(f"Loading chatbot model: {model_name}")
|
| 100 |
+
try:
|
| 101 |
+
self.tokenizer = AutoTokenizer.from_pretrained(model_name)
|
| 102 |
+
self.model = AutoModelForCausalLM.from_pretrained(model_name)
|
| 103 |
+
self.tokenizer.pad_token = self.tokenizer.eos_token
|
| 104 |
+
except Exception as e:
|
| 105 |
+
print(f"Warning: Could not load {model_name}. Using a simpler pipeline.")
|
| 106 |
+
self.model = None
|
| 107 |
+
self.tokenizer = None
|
| 108 |
+
self.chatbot_pipeline = pipeline(
|
| 109 |
+
"text-generation",
|
| 110 |
+
model="gpt2",
|
| 111 |
+
device=0 if self.device == "cuda" else -1
|
| 112 |
+
)
|
| 113 |
+
|
| 114 |
+
# Initialize document ingestion
|
| 115 |
+
self.ingestion = DocumentIngestion(embedding_model=embedding_model)
|
| 116 |
+
|
| 117 |
+
# Load vector store if it exists
|
| 118 |
+
if os.path.exists(vector_store_path) and os.path.exists(
|
| 119 |
+
os.path.join(vector_store_path, "index.faiss")
|
| 120 |
+
):
|
| 121 |
+
try:
|
| 122 |
+
self.ingestion.load(vector_store_path)
|
| 123 |
+
print("Loaded existing vector store")
|
| 124 |
+
except Exception as e:
|
| 125 |
+
print(f"Could not load vector store: {e}")
|
| 126 |
+
|
| 127 |
+
self.chat_history = []
|
| 128 |
+
|
| 129 |
+
def generate_response(self, query: str, use_rag: bool = True, num_results: int = 3) -> str:
|
| 130 |
+
"""
|
| 131 |
+
Generate a response to the user query.
|
| 132 |
+
|
| 133 |
+
Args:
|
| 134 |
+
query: User's question
|
| 135 |
+
use_rag: Whether to use RAG (retrieve relevant documents)
|
| 136 |
+
num_results: Number of document chunks to retrieve
|
| 137 |
+
|
| 138 |
+
Returns:
|
| 139 |
+
Generated response
|
| 140 |
+
"""
|
| 141 |
+
# If RAG is enabled and we have a vector store, return relevant context
|
| 142 |
+
if use_rag and self.ingestion.index is not None:
|
| 143 |
+
try:
|
| 144 |
+
results = self.ingestion.search(query, k=num_results)
|
| 145 |
+
if results:
|
| 146 |
+
# Format the response with relevant context
|
| 147 |
+
response_parts = []
|
| 148 |
+
response_parts.append(f"Based on the documents, here's what I found regarding your question: '{query}'\n\n")
|
| 149 |
+
|
| 150 |
+
for i, result in enumerate(results, 1):
|
| 151 |
+
source = result['metadata']['source']
|
| 152 |
+
text = result['text']
|
| 153 |
+
# Clean up the text
|
| 154 |
+
text = text.strip()
|
| 155 |
+
if text:
|
| 156 |
+
response_parts.append(f"**Relevant information {i}** (from {source}):\n{text}\n")
|
| 157 |
+
|
| 158 |
+
response = "\n".join(response_parts)
|
| 159 |
+
return response
|
| 160 |
+
except Exception as e:
|
| 161 |
+
print(f"Error in RAG retrieval: {e}")
|
| 162 |
+
return f"I encountered an error while searching the documents: {str(e)}"
|
| 163 |
+
|
| 164 |
+
# If no RAG or no results, try to generate a response using the model
|
| 165 |
+
# But DialoGPT isn't great for this, so we'll keep it simple
|
| 166 |
+
if self.model and self.tokenizer:
|
| 167 |
+
# Simple generation without complex prompts
|
| 168 |
+
inputs = self.tokenizer.encode(query, return_tensors="pt")
|
| 169 |
+
inputs = inputs.to(self.device)
|
| 170 |
+
|
| 171 |
+
with torch.no_grad():
|
| 172 |
+
outputs = self.model.generate(
|
| 173 |
+
inputs,
|
| 174 |
+
max_new_tokens=100,
|
| 175 |
+
num_return_sequences=1,
|
| 176 |
+
temperature=0.7,
|
| 177 |
+
do_sample=True,
|
| 178 |
+
pad_token_id=self.tokenizer.eos_token_id,
|
| 179 |
+
eos_token_id=self.tokenizer.eos_token_id,
|
| 180 |
+
)
|
| 181 |
+
|
| 182 |
+
# Decode only new tokens
|
| 183 |
+
input_length = inputs.shape[1]
|
| 184 |
+
generated_tokens = outputs[0][input_length:]
|
| 185 |
+
response = self.tokenizer.decode(generated_tokens, skip_special_tokens=True)
|
| 186 |
+
|
| 187 |
+
# Clean up
|
| 188 |
+
response = response.replace("<|endoftext|>", "").strip()
|
| 189 |
+
|
| 190 |
+
if not response or len(response.strip()) < 3:
|
| 191 |
+
return "I understand your question, but I don't have relevant information in my knowledge base. Please enable RAG to search the documents."
|
| 192 |
+
|
| 193 |
+
return response
|
| 194 |
+
else:
|
| 195 |
+
return "I understand your question, but I don't have relevant information in my knowledge base. Please enable RAG to search the documents."
|
| 196 |
+
|
| 197 |
+
def chat(self, message: str, history, use_rag: bool):
|
| 198 |
+
"""
|
| 199 |
+
Handle chat interaction.
|
| 200 |
+
|
| 201 |
+
Args:
|
| 202 |
+
message: User message
|
| 203 |
+
history: Chat history (list of ChatMessage or dicts with 'role' and 'content')
|
| 204 |
+
use_rag: Whether to use RAG
|
| 205 |
+
|
| 206 |
+
Returns:
|
| 207 |
+
Updated history
|
| 208 |
+
"""
|
| 209 |
+
if not message or not message.strip():
|
| 210 |
+
return "", history or []
|
| 211 |
+
|
| 212 |
+
# Ensure history is a list
|
| 213 |
+
if history is None:
|
| 214 |
+
history = []
|
| 215 |
+
|
| 216 |
+
# Add user message as dictionary
|
| 217 |
+
history.append({"role": "user", "content": message})
|
| 218 |
+
|
| 219 |
+
# Generate response
|
| 220 |
+
try:
|
| 221 |
+
response = self.generate_response(message, use_rag=use_rag)
|
| 222 |
+
# Ensure response is not empty
|
| 223 |
+
if not response or not response.strip():
|
| 224 |
+
response = "I'm sorry, I couldn't generate a response. Please try again."
|
| 225 |
+
except Exception as e:
|
| 226 |
+
print(f"Error generating response: {e}")
|
| 227 |
+
import traceback
|
| 228 |
+
traceback.print_exc()
|
| 229 |
+
response = f"I encountered an error: {str(e)}"
|
| 230 |
+
|
| 231 |
+
# Add assistant response as dictionary
|
| 232 |
+
history.append({"role": "assistant", "content": response})
|
| 233 |
+
|
| 234 |
+
print(f"Debug - History length: {len(history)}")
|
| 235 |
+
print(f"Debug - Response: {response[:100] if response else 'None'}...")
|
| 236 |
+
|
| 237 |
+
return "", history
|
| 238 |
+
|
| 239 |
+
|
| 240 |
+
# Initialize chatbot
|
| 241 |
+
chatbot = RAGChatbot()
|
| 242 |
+
|
| 243 |
+
|
| 244 |
+
# Create Gradio interface
|
| 245 |
+
with gr.Blocks(title="Hugging Face RAG Chatbot") as app:
|
| 246 |
+
gr.Markdown("# 🤖 Hugging Face RAG Chatbot")
|
| 247 |
+
gr.Markdown("Chat with your documents! Run `ingest_documents.py` to update the knowledge base.")
|
| 248 |
+
|
| 249 |
+
chatbot_interface = gr.Chatbot(
|
| 250 |
+
label="Chat",
|
| 251 |
+
height=500,
|
| 252 |
+
value=[] # Initialize with empty list
|
| 253 |
+
)
|
| 254 |
+
|
| 255 |
+
with gr.Row():
|
| 256 |
+
msg = gr.Textbox(
|
| 257 |
+
label="Your Message",
|
| 258 |
+
placeholder="Ask a question about your documents...",
|
| 259 |
+
scale=4
|
| 260 |
+
)
|
| 261 |
+
use_rag = gr.Checkbox(
|
| 262 |
+
label="Use RAG",
|
| 263 |
+
value=True,
|
| 264 |
+
scale=1
|
| 265 |
+
)
|
| 266 |
+
|
| 267 |
+
with gr.Row():
|
| 268 |
+
submit_btn = gr.Button("Send", variant="primary")
|
| 269 |
+
clear_btn = gr.Button("Clear")
|
| 270 |
+
|
| 271 |
+
msg.submit(
|
| 272 |
+
chatbot.chat,
|
| 273 |
+
inputs=[msg, chatbot_interface, use_rag],
|
| 274 |
+
outputs=[msg, chatbot_interface]
|
| 275 |
+
)
|
| 276 |
+
submit_btn.click(
|
| 277 |
+
chatbot.chat,
|
| 278 |
+
inputs=[msg, chatbot_interface, use_rag],
|
| 279 |
+
outputs=[msg, chatbot_interface]
|
| 280 |
+
)
|
| 281 |
+
def clear_chat():
|
| 282 |
+
return [], ""
|
| 283 |
+
|
| 284 |
+
clear_btn.click(clear_chat, outputs=[chatbot_interface, msg])
|
| 285 |
+
|
| 286 |
+
|
| 287 |
+
if __name__ == "__main__":
|
| 288 |
+
# Get port from environment variable (Hugging Face Spaces sets this) or default to 7860
|
| 289 |
+
port = int(os.environ.get("PORT", 7860))
|
| 290 |
+
app.launch(
|
| 291 |
+
share=False,
|
| 292 |
+
server_name="0.0.0.0",
|
| 293 |
+
server_port=port,
|
| 294 |
+
theme=MinimalistTheme()
|
| 295 |
+
)
|
example_usage.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Example script showing how to use the document ingestion system programmatically.
|
| 3 |
+
"""
|
| 4 |
+
from ingestion import DocumentIngestion
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
def main():
|
| 8 |
+
# Initialize the ingestion system
|
| 9 |
+
ingestion = DocumentIngestion(embedding_model="all-MiniLM-L6-v2")
|
| 10 |
+
|
| 11 |
+
# Example 1: Process PDFs
|
| 12 |
+
pdf_paths = [
|
| 13 |
+
# Add your PDF file paths here
|
| 14 |
+
# "path/to/document1.pdf",
|
| 15 |
+
# "path/to/document2.pdf",
|
| 16 |
+
]
|
| 17 |
+
|
| 18 |
+
# Example 2: Process URLs
|
| 19 |
+
urls = [
|
| 20 |
+
# Add URLs here
|
| 21 |
+
# "https://en.wikipedia.org/wiki/Artificial_intelligence",
|
| 22 |
+
# "https://huggingface.co/docs/transformers",
|
| 23 |
+
]
|
| 24 |
+
|
| 25 |
+
# Process documents
|
| 26 |
+
if pdf_paths or urls:
|
| 27 |
+
print("Processing documents...")
|
| 28 |
+
documents = ingestion.process_documents(pdf_paths=pdf_paths, urls=urls)
|
| 29 |
+
print(f"Processed {len(documents)} document chunks")
|
| 30 |
+
|
| 31 |
+
# Build vector store
|
| 32 |
+
ingestion.build_vector_store()
|
| 33 |
+
|
| 34 |
+
# Save vector store
|
| 35 |
+
ingestion.save("./vector_store")
|
| 36 |
+
|
| 37 |
+
# Example search
|
| 38 |
+
query = "What is artificial intelligence?"
|
| 39 |
+
results = ingestion.search(query, k=3)
|
| 40 |
+
|
| 41 |
+
print(f"\nSearch results for: '{query}'")
|
| 42 |
+
print("-" * 50)
|
| 43 |
+
for i, result in enumerate(results, 1):
|
| 44 |
+
print(f"\nResult {i}:")
|
| 45 |
+
print(f"Source: {result['metadata']['source']}")
|
| 46 |
+
print(f"Score: {result['score']:.4f}")
|
| 47 |
+
print(f"Text: {result['text'][:200]}...")
|
| 48 |
+
else:
|
| 49 |
+
print("Please add PDF paths or URLs to the script to test ingestion.")
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
if __name__ == "__main__":
|
| 53 |
+
main()
|
ingest_documents.py
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Standalone script to ingest PDFs and URLs into the vector store.
|
| 3 |
+
Run this script periodically to update your document knowledge base.
|
| 4 |
+
"""
|
| 5 |
+
import os
|
| 6 |
+
from pathlib import Path
|
| 7 |
+
from ingestion import DocumentIngestion
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
# Configuration
|
| 11 |
+
PDF_FOLDER = "./pdfs" # Folder containing PDF files
|
| 12 |
+
URLS = [
|
| 13 |
+
# Add your URLs here, one per line
|
| 14 |
+
"https://www.ontario.ca/page/organic-crop-and-livestock-production-ontario"
|
| 15 |
+
]
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
def main():
|
| 19 |
+
"""Main ingestion function."""
|
| 20 |
+
print("=" * 60)
|
| 21 |
+
print("Document Ingestion Script")
|
| 22 |
+
print("=" * 60)
|
| 23 |
+
|
| 24 |
+
# Initialize ingestion system
|
| 25 |
+
print("\nInitializing document ingestion system...")
|
| 26 |
+
ingestion = DocumentIngestion(embedding_model="all-MiniLM-L6-v2")
|
| 27 |
+
|
| 28 |
+
# Collect PDF files
|
| 29 |
+
pdf_paths = []
|
| 30 |
+
if os.path.exists(PDF_FOLDER):
|
| 31 |
+
pdf_files = list(Path(PDF_FOLDER).glob("*.pdf"))
|
| 32 |
+
pdf_paths = [str(f) for f in pdf_files]
|
| 33 |
+
print(f"\nFound {len(pdf_paths)} PDF file(s) in {PDF_FOLDER}:")
|
| 34 |
+
for pdf in pdf_paths:
|
| 35 |
+
print(f" - {os.path.basename(pdf)}")
|
| 36 |
+
else:
|
| 37 |
+
print(f"\nPDF folder '{PDF_FOLDER}' not found. Creating it...")
|
| 38 |
+
os.makedirs(PDF_FOLDER, exist_ok=True)
|
| 39 |
+
print(f"Please add PDF files to {PDF_FOLDER} and run again.")
|
| 40 |
+
|
| 41 |
+
# Filter out empty URLs
|
| 42 |
+
urls = [url.strip() for url in URLS if url.strip()]
|
| 43 |
+
|
| 44 |
+
if urls:
|
| 45 |
+
print(f"\nFound {len(urls)} URL(s) to process:")
|
| 46 |
+
for url in urls:
|
| 47 |
+
print(f" - {url}")
|
| 48 |
+
else:
|
| 49 |
+
print("\nNo URLs configured. Add URLs to the URLS list in this script.")
|
| 50 |
+
|
| 51 |
+
if not pdf_paths and not urls:
|
| 52 |
+
print("\n[ERROR] No documents to process. Please add PDFs or URLs.")
|
| 53 |
+
return
|
| 54 |
+
|
| 55 |
+
# Process documents
|
| 56 |
+
print("\n" + "=" * 60)
|
| 57 |
+
print("Processing documents...")
|
| 58 |
+
print("=" * 60)
|
| 59 |
+
|
| 60 |
+
try:
|
| 61 |
+
documents = ingestion.process_documents(pdf_paths=pdf_paths, urls=urls)
|
| 62 |
+
print(f"\n[SUCCESS] Successfully processed {len(documents)} document chunks")
|
| 63 |
+
|
| 64 |
+
# Build vector store
|
| 65 |
+
print("\nBuilding vector store...")
|
| 66 |
+
ingestion.build_vector_store()
|
| 67 |
+
|
| 68 |
+
# Save vector store
|
| 69 |
+
print("\nSaving vector store...")
|
| 70 |
+
ingestion.save("./vector_store")
|
| 71 |
+
|
| 72 |
+
print("\n" + "=" * 60)
|
| 73 |
+
print("[SUCCESS] Ingestion complete!")
|
| 74 |
+
print("=" * 60)
|
| 75 |
+
print(f"\nTotal document chunks: {len(documents)}")
|
| 76 |
+
print(f"Vector store saved to: ./vector_store")
|
| 77 |
+
print("\nYou can now run 'py app.py' to start the chatbot.")
|
| 78 |
+
|
| 79 |
+
except Exception as e:
|
| 80 |
+
print(f"\n[ERROR] Error during ingestion: {str(e)}")
|
| 81 |
+
import traceback
|
| 82 |
+
traceback.print_exc()
|
| 83 |
+
return
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
if __name__ == "__main__":
|
| 87 |
+
main()
|
ingestion.py
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Document ingestion module for processing PDFs and URLs.
|
| 3 |
+
"""
|
| 4 |
+
import os
|
| 5 |
+
from typing import List, Dict
|
| 6 |
+
from pathlib import Path
|
| 7 |
+
import requests
|
| 8 |
+
from bs4 import BeautifulSoup
|
| 9 |
+
from pypdf import PdfReader
|
| 10 |
+
from langchain_text_splitters import RecursiveCharacterTextSplitter
|
| 11 |
+
from sentence_transformers import SentenceTransformer
|
| 12 |
+
import numpy as np
|
| 13 |
+
import faiss
|
| 14 |
+
import pickle
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
class DocumentIngestion:
|
| 18 |
+
"""Handles ingestion of PDFs and URLs into a searchable vector store."""
|
| 19 |
+
|
| 20 |
+
def __init__(self, embedding_model: str = "all-MiniLM-L6-v2"):
|
| 21 |
+
"""
|
| 22 |
+
Initialize the document ingestion system.
|
| 23 |
+
|
| 24 |
+
Args:
|
| 25 |
+
embedding_model: Hugging Face model name for embeddings
|
| 26 |
+
"""
|
| 27 |
+
self.embedding_model = SentenceTransformer(embedding_model)
|
| 28 |
+
self.text_splitter = RecursiveCharacterTextSplitter(
|
| 29 |
+
chunk_size=1000,
|
| 30 |
+
chunk_overlap=200,
|
| 31 |
+
length_function=len,
|
| 32 |
+
)
|
| 33 |
+
self.documents = []
|
| 34 |
+
self.embeddings = None
|
| 35 |
+
self.index = None
|
| 36 |
+
|
| 37 |
+
def read_pdf(self, file_path: str) -> str:
|
| 38 |
+
"""
|
| 39 |
+
Extract text from a PDF file.
|
| 40 |
+
|
| 41 |
+
Args:
|
| 42 |
+
file_path: Path to the PDF file
|
| 43 |
+
|
| 44 |
+
Returns:
|
| 45 |
+
Extracted text content
|
| 46 |
+
"""
|
| 47 |
+
try:
|
| 48 |
+
reader = PdfReader(file_path)
|
| 49 |
+
text = ""
|
| 50 |
+
for page in reader.pages:
|
| 51 |
+
text += page.extract_text() + "\n"
|
| 52 |
+
return text
|
| 53 |
+
except Exception as e:
|
| 54 |
+
raise Exception(f"Error reading PDF {file_path}: {str(e)}")
|
| 55 |
+
|
| 56 |
+
def read_url(self, url: str) -> str:
|
| 57 |
+
"""
|
| 58 |
+
Extract text from a URL.
|
| 59 |
+
|
| 60 |
+
Args:
|
| 61 |
+
url: URL to fetch and extract text from
|
| 62 |
+
|
| 63 |
+
Returns:
|
| 64 |
+
Extracted text content
|
| 65 |
+
"""
|
| 66 |
+
try:
|
| 67 |
+
headers = {
|
| 68 |
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
|
| 69 |
+
}
|
| 70 |
+
response = requests.get(url, headers=headers, timeout=10)
|
| 71 |
+
response.raise_for_status()
|
| 72 |
+
|
| 73 |
+
soup = BeautifulSoup(response.content, 'html.parser')
|
| 74 |
+
|
| 75 |
+
# Remove script and style elements
|
| 76 |
+
for script in soup(["script", "style"]):
|
| 77 |
+
script.decompose()
|
| 78 |
+
|
| 79 |
+
# Get text
|
| 80 |
+
text = soup.get_text()
|
| 81 |
+
|
| 82 |
+
# Clean up whitespace
|
| 83 |
+
lines = (line.strip() for line in text.splitlines())
|
| 84 |
+
chunks = (phrase.strip() for line in lines for phrase in line.split(" "))
|
| 85 |
+
text = ' '.join(chunk for chunk in chunks if chunk)
|
| 86 |
+
|
| 87 |
+
return text
|
| 88 |
+
except Exception as e:
|
| 89 |
+
raise Exception(f"Error reading URL {url}: {str(e)}")
|
| 90 |
+
|
| 91 |
+
def process_documents(self, pdf_paths: List[str] = None, urls: List[str] = None) -> List[Dict]:
|
| 92 |
+
"""
|
| 93 |
+
Process PDFs and URLs into chunks.
|
| 94 |
+
|
| 95 |
+
Args:
|
| 96 |
+
pdf_paths: List of PDF file paths
|
| 97 |
+
urls: List of URLs to process
|
| 98 |
+
|
| 99 |
+
Returns:
|
| 100 |
+
List of document chunks with metadata
|
| 101 |
+
"""
|
| 102 |
+
all_texts = []
|
| 103 |
+
all_metadata = []
|
| 104 |
+
|
| 105 |
+
# Process PDFs
|
| 106 |
+
if pdf_paths:
|
| 107 |
+
for pdf_path in pdf_paths:
|
| 108 |
+
if not os.path.exists(pdf_path):
|
| 109 |
+
print(f"Warning: PDF file not found: {pdf_path}")
|
| 110 |
+
continue
|
| 111 |
+
|
| 112 |
+
text = self.read_pdf(pdf_path)
|
| 113 |
+
chunks = self.text_splitter.split_text(text)
|
| 114 |
+
|
| 115 |
+
for i, chunk in enumerate(chunks):
|
| 116 |
+
all_texts.append(chunk)
|
| 117 |
+
all_metadata.append({
|
| 118 |
+
'source': pdf_path,
|
| 119 |
+
'type': 'pdf',
|
| 120 |
+
'chunk_index': i
|
| 121 |
+
})
|
| 122 |
+
|
| 123 |
+
# Process URLs
|
| 124 |
+
if urls:
|
| 125 |
+
for url in urls:
|
| 126 |
+
try:
|
| 127 |
+
text = self.read_url(url)
|
| 128 |
+
chunks = self.text_splitter.split_text(text)
|
| 129 |
+
|
| 130 |
+
for i, chunk in enumerate(chunks):
|
| 131 |
+
all_texts.append(chunk)
|
| 132 |
+
all_metadata.append({
|
| 133 |
+
'source': url,
|
| 134 |
+
'type': 'url',
|
| 135 |
+
'chunk_index': i
|
| 136 |
+
})
|
| 137 |
+
except Exception as e:
|
| 138 |
+
print(f"Warning: Failed to process URL {url}: {str(e)}")
|
| 139 |
+
continue
|
| 140 |
+
|
| 141 |
+
# Create document objects
|
| 142 |
+
documents = []
|
| 143 |
+
for text, metadata in zip(all_texts, all_metadata):
|
| 144 |
+
documents.append({
|
| 145 |
+
'text': text,
|
| 146 |
+
'metadata': metadata
|
| 147 |
+
})
|
| 148 |
+
|
| 149 |
+
self.documents = documents
|
| 150 |
+
return documents
|
| 151 |
+
|
| 152 |
+
def build_vector_store(self):
|
| 153 |
+
"""Build FAISS vector store from processed documents."""
|
| 154 |
+
if not self.documents:
|
| 155 |
+
raise ValueError("No documents processed. Call process_documents() first.")
|
| 156 |
+
|
| 157 |
+
# Extract texts
|
| 158 |
+
texts = [doc['text'] for doc in self.documents]
|
| 159 |
+
|
| 160 |
+
# Generate embeddings
|
| 161 |
+
print("Generating embeddings...")
|
| 162 |
+
self.embeddings = self.embedding_model.encode(texts, show_progress_bar=True)
|
| 163 |
+
|
| 164 |
+
# Build FAISS index
|
| 165 |
+
dimension = self.embeddings.shape[1]
|
| 166 |
+
self.index = faiss.IndexFlatL2(dimension)
|
| 167 |
+
self.index.add(self.embeddings.astype('float32'))
|
| 168 |
+
|
| 169 |
+
print(f"Vector store built with {len(self.documents)} documents")
|
| 170 |
+
|
| 171 |
+
def search(self, query: str, k: int = 5) -> List[Dict]:
|
| 172 |
+
"""
|
| 173 |
+
Search for similar documents.
|
| 174 |
+
|
| 175 |
+
Args:
|
| 176 |
+
query: Search query
|
| 177 |
+
k: Number of results to return
|
| 178 |
+
|
| 179 |
+
Returns:
|
| 180 |
+
List of relevant document chunks with scores
|
| 181 |
+
"""
|
| 182 |
+
if self.index is None:
|
| 183 |
+
raise ValueError("Vector store not built. Call build_vector_store() first.")
|
| 184 |
+
|
| 185 |
+
# Encode query
|
| 186 |
+
query_embedding = self.embedding_model.encode([query])
|
| 187 |
+
|
| 188 |
+
# Search
|
| 189 |
+
distances, indices = self.index.search(query_embedding.astype('float32'), k)
|
| 190 |
+
|
| 191 |
+
# Format results
|
| 192 |
+
results = []
|
| 193 |
+
for i, idx in enumerate(indices[0]):
|
| 194 |
+
if idx < len(self.documents):
|
| 195 |
+
results.append({
|
| 196 |
+
'text': self.documents[idx]['text'],
|
| 197 |
+
'metadata': self.documents[idx]['metadata'],
|
| 198 |
+
'score': float(distances[0][i])
|
| 199 |
+
})
|
| 200 |
+
|
| 201 |
+
return results
|
| 202 |
+
|
| 203 |
+
def save(self, directory: str = "./vector_store"):
|
| 204 |
+
"""Save the vector store to disk."""
|
| 205 |
+
os.makedirs(directory, exist_ok=True)
|
| 206 |
+
|
| 207 |
+
# Save index
|
| 208 |
+
faiss.write_index(self.index, os.path.join(directory, "index.faiss"))
|
| 209 |
+
|
| 210 |
+
# Save documents and embeddings
|
| 211 |
+
with open(os.path.join(directory, "documents.pkl"), "wb") as f:
|
| 212 |
+
pickle.dump(self.documents, f)
|
| 213 |
+
|
| 214 |
+
with open(os.path.join(directory, "embeddings.pkl"), "wb") as f:
|
| 215 |
+
pickle.dump(self.embeddings, f)
|
| 216 |
+
|
| 217 |
+
print(f"Vector store saved to {directory}")
|
| 218 |
+
|
| 219 |
+
def load(self, directory: str = "./vector_store"):
|
| 220 |
+
"""Load the vector store from disk."""
|
| 221 |
+
# Load index
|
| 222 |
+
self.index = faiss.read_index(os.path.join(directory, "index.faiss"))
|
| 223 |
+
|
| 224 |
+
# Load documents and embeddings
|
| 225 |
+
with open(os.path.join(directory, "documents.pkl"), "rb") as f:
|
| 226 |
+
self.documents = pickle.load(f)
|
| 227 |
+
|
| 228 |
+
with open(os.path.join(directory, "embeddings.pkl"), "rb") as f:
|
| 229 |
+
self.embeddings = pickle.load(f)
|
| 230 |
+
|
| 231 |
+
print(f"Vector store loaded from {directory}")
|
pdfs/.gitkeep
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file ensures the pdfs folder is tracked in git
|
| 2 |
+
# Add your PDF files to this folder
|
requirements.txt
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
gradio>=4.0.0
|
| 2 |
+
transformers>=4.35.0
|
| 3 |
+
torch>=2.0.0
|
| 4 |
+
sentence-transformers>=2.2.0
|
| 5 |
+
langchain>=0.1.0
|
| 6 |
+
langchain-community>=0.0.20
|
| 7 |
+
pypdf>=3.17.0
|
| 8 |
+
beautifulsoup4>=4.12.0
|
| 9 |
+
requests>=2.31.0
|
| 10 |
+
faiss-cpu>=1.7.4
|
| 11 |
+
numpy>=1.24.0
|
| 12 |
+
accelerate>=0.25.0
|