Ryan commited on
Commit
2132962
Β·
1 Parent(s): 7d34386

simplify changes

Browse files
.gitignore CHANGED
@@ -35,7 +35,7 @@ env/
35
  *.swo
36
  *~
37
 
38
- # Data files (don't commit large data files to HF Spaces)
39
  articles.json
40
  article_chunks.jsonl
41
  validation_results.json
@@ -46,3 +46,5 @@ Thumbs.db
46
 
47
  # Gradio
48
  flagged/
 
 
 
35
  *.swo
36
  *~
37
 
38
+ # Data files (large, don't commit)
39
  articles.json
40
  article_chunks.jsonl
41
  validation_results.json
 
46
 
47
  # Gradio
48
  flagged/
49
+
50
+ TODO.txt
CHANGES_SUMMARY.md DELETED
@@ -1,143 +0,0 @@
1
- # 🎯 Summary: Hugging Face Spaces Setup Complete
2
-
3
- ## βœ… What Was Done
4
-
5
- Your project is now **fully configured** for deployment to Hugging Face Spaces!
6
-
7
- ### Files Created/Modified
8
-
9
- 1. **`app.py`** ✨ NEW
10
- - Main Gradio interface optimized for HF Spaces
11
- - Removed server configuration (HF handles this)
12
- - Clean launch() call for HF environment
13
-
14
- 2. **`README.md`** ✏️ UPDATED
15
- - Added HF Spaces YAML frontmatter
16
- - Included deployment instructions
17
- - Added configuration guide for Secrets
18
-
19
- 3. **`.gitignore`** ✨ NEW
20
- - Excludes sensitive files (.env, data files)
21
- - HF Spaces best practices
22
-
23
- 4. **`requirements.txt`** ✏️ UPDATED
24
- - Added torch dependency (needed by sentence-transformers)
25
- - All dependencies verified for HF Spaces
26
-
27
- ### Documentation Created
28
-
29
- 5. **`DEPLOYMENT.md`** ✨ NEW
30
- - Complete step-by-step deployment guide
31
- - Troubleshooting section
32
- - Cost breakdown
33
-
34
- 6. **`HF_SPACES_CHECKLIST.md`** ✨ NEW
35
- - Detailed checklist for deployment
36
- - File exclusion list
37
- - Common issues and solutions
38
-
39
- 7. **`QUICK_START.md`** ✨ NEW
40
- - 5-minute quick start guide
41
- - TL;DR version for fast deployment
42
- - Quick reference table
43
-
44
- 8. **`CHANGES_SUMMARY.md`** ✨ NEW (this file)
45
- - Overview of all changes made
46
-
47
- ### Helper Scripts
48
-
49
- 9. **`prepare_deployment.sh`** ✨ NEW
50
- - Automated script to copy deployment files
51
- - Already tested and working!
52
- - Creates `hf_spaces_deploy/` directory
53
-
54
- ### Ready-to-Deploy Files
55
-
56
- The `hf_spaces_deploy/` directory contains exactly what you need:
57
- ```
58
- hf_spaces_deploy/
59
- β”œβ”€β”€ app.py (3.3K)
60
- β”œβ”€β”€ rag_chat.py (5.6K)
61
- β”œβ”€β”€ citation_validator.py (13K)
62
- β”œβ”€β”€ config.py (252B)
63
- β”œβ”€β”€ requirements.txt (170B)
64
- └── README.md (2.9K)
65
- ```
66
-
67
- ## πŸš€ Next Steps (Your Action Required)
68
-
69
- ### Quick Deploy (5 minutes):
70
-
71
- 1. **Create HF Space**: https://huggingface.co/spaces β†’ "Create new Space"
72
- - Choose Gradio SDK
73
- - Use CPU basic (free)
74
-
75
- 2. **Add Secrets** in Space Settings:
76
- - `QDRANT_URL`
77
- - `QDRANT_API_KEY`
78
- - `OPENAI_API_KEY`
79
-
80
- 3. **Upload files** from `hf_spaces_deploy/` folder
81
-
82
- 4. **Done!** Your app will be live in 2-5 minutes
83
-
84
- ### Detailed Instructions:
85
- - See `QUICK_START.md` for step-by-step guide
86
- - See `HF_SPACES_CHECKLIST.md` for complete checklist
87
- - See `DEPLOYMENT.md` for troubleshooting
88
-
89
- ## πŸ“Š What Stays Local
90
-
91
- These files are for local development only (NOT uploaded to HF Spaces):
92
- - `.env` - Your secrets (use HF Secrets instead)
93
- - `articles.json` - Source data (already in Qdrant)
94
- - `article_chunks.jsonl` - Chunked data (already in Qdrant)
95
- - `web_app.py` - Old version (replaced by app.py)
96
- - `*_cli.py` - Setup scripts (not needed in deployment)
97
-
98
- ## ✨ Key Features of Your Deployment
99
-
100
- - βœ… **Free hosting** on HF Spaces CPU tier
101
- - βœ… **Secure** - API keys stored as Secrets
102
- - βœ… **Fast** - Optimized for Gradio 4.0+
103
- - βœ… **Professional** - Beautiful UI with Soft theme
104
- - βœ… **Validated citations** - Every quote is verified
105
- - βœ… **Easy updates** - Just git push to redeploy
106
-
107
- ## πŸŽ“ Architecture
108
-
109
- ```
110
- User Question
111
- ↓
112
- [Gradio UI (app.py)]
113
- ↓
114
- [RAG Logic (rag_chat.py)]
115
- ↓
116
- [Qdrant Vector DB] β†’ Retrieve relevant chunks
117
- ↓
118
- [OpenAI GPT-4o-mini] β†’ Generate answer with citations
119
- ↓
120
- [Citation Validator] β†’ Verify quotes against sources
121
- ↓
122
- [Formatted Response] β†’ Display to user
123
- ```
124
-
125
- ## πŸ“ Notes
126
-
127
- - Environment variables work automatically on HF Spaces (no .env needed)
128
- - `load_dotenv()` gracefully handles missing .env file
129
- - All code is production-ready and tested
130
- - Deployment is reversible (just delete the Space)
131
-
132
- ## πŸ€” Questions?
133
-
134
- Refer to:
135
- 1. `QUICK_START.md` - Fast deployment
136
- 2. `HF_SPACES_CHECKLIST.md` - Detailed checklist
137
- 3. `DEPLOYMENT.md` - Complete guide
138
- 4. HF Spaces docs: https://huggingface.co/docs/hub/spaces
139
-
140
- ---
141
-
142
- **Your project is 100% ready for Hugging Face Spaces! πŸš€**
143
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
HF_SPACES_CHECKLIST.md DELETED
@@ -1,140 +0,0 @@
1
- # Hugging Face Spaces Deployment Checklist
2
-
3
- ## βœ… Files Ready for Upload
4
-
5
- These are the ONLY files you need to upload to Hugging Face Spaces:
6
-
7
- - [ ] `app.py` - Main Gradio interface (βœ“ Created)
8
- - [ ] `rag_chat.py` - RAG logic
9
- - [ ] `citation_validator.py` - Citation validation
10
- - [ ] `config.py` - Configuration constants
11
- - [ ] `requirements.txt` - Python dependencies (βœ“ Updated)
12
- - [ ] `README.md` - Documentation with HF metadata (βœ“ Updated)
13
-
14
- ## ❌ Files to EXCLUDE (Do NOT upload)
15
-
16
- - `.env` - Contains secrets (use HF Spaces Secrets instead)
17
- - `articles.json` - Large data file (not needed, data is in Qdrant)
18
- - `article_chunks.jsonl` - Large data file (not needed, data is in Qdrant)
19
- - `validation_results.json` - Runtime output file
20
- - `__pycache__/` - Python cache
21
- - `web_app.py` - Old version (replaced by app.py)
22
- - `extract_articles_cli.py` - Setup script (not needed for deployed app)
23
- - `chunk_articles_cli.py` - Setup script (not needed for deployed app)
24
- - `upload_to_qdrant_cli.py` - Setup script (not needed for deployed app)
25
-
26
- ## πŸ”§ Pre-Deployment Steps
27
-
28
- ### 1. Verify Data is in Qdrant
29
- ```bash
30
- # Make sure you've already run these locally:
31
- python extract_articles_cli.py
32
- python chunk_articles_cli.py
33
- python upload_to_qdrant_cli.py
34
- ```
35
-
36
- ### 2. Test Locally (Optional)
37
- ```bash
38
- # Set up virtual environment
39
- python -m venv venv
40
- source venv/bin/activate # On Windows: venv\Scripts\activate
41
-
42
- # Install dependencies
43
- pip install -r requirements.txt
44
-
45
- # Run the app
46
- python app.py
47
- ```
48
-
49
- ### 3. Create Hugging Face Space
50
- 1. Go to https://huggingface.co/spaces
51
- 2. Click "Create new Space"
52
- 3. Configure:
53
- - **Space name**: Your choice (e.g., `80k-career-advisor`)
54
- - **SDK**: Gradio
55
- - **Hardware**: CPU basic (free tier is sufficient)
56
- - **Visibility**: Public or Private
57
-
58
- ### 4. Configure Secrets (CRITICAL!)
59
- In your Space Settings β†’ Variables and Secrets, add:
60
-
61
- - **QDRANT_URL**: `https://your-cluster-url.aws.cloud.qdrant.io`
62
- - **QDRANT_API_KEY**: `your-qdrant-api-key`
63
- - **OPENAI_API_KEY**: `sk-...your-openai-key`
64
-
65
- ### 5. Upload Files
66
-
67
- **Option A: Git**
68
- ```bash
69
- git clone https://huggingface.co/spaces/YOUR_USERNAME/YOUR_SPACE_NAME
70
- cd YOUR_SPACE_NAME
71
-
72
- # Copy only the necessary files
73
- cp /home/ryan/Documents/80k_rag/app.py .
74
- cp /home/ryan/Documents/80k_rag/rag_chat.py .
75
- cp /home/ryan/Documents/80k_rag/citation_validator.py .
76
- cp /home/ryan/Documents/80k_rag/config.py .
77
- cp /home/ryan/Documents/80k_rag/requirements.txt .
78
- cp /home/ryan/Documents/80k_rag/README.md .
79
-
80
- git add .
81
- git commit -m "Initial deployment"
82
- git push
83
- ```
84
-
85
- **Option B: Web Interface**
86
- 1. Click "Files and versions" tab
87
- 2. Click "Upload files"
88
- 3. Drag and drop the 6 files listed above
89
- 4. Click "Commit"
90
-
91
- ### 6. Monitor Build
92
- - Watch the build logs in the App tab
93
- - Build typically takes 2-5 minutes
94
- - Look for any errors in dependencies or imports
95
-
96
- ## πŸš€ Post-Deployment
97
-
98
- ### Testing
99
- 1. Visit your Space URL: `https://huggingface.co/spaces/YOUR_USERNAME/YOUR_SPACE_NAME`
100
- 2. Try the example questions
101
- 3. Test with a custom question
102
- 4. Verify citations are displaying correctly
103
-
104
- ### Common Issues
105
-
106
- **Problem**: Build fails with "Module not found"
107
- - **Solution**: Check that all imports in `app.py`, `rag_chat.py`, and `citation_validator.py` are in `requirements.txt`
108
-
109
- **Problem**: Runtime error about missing API keys
110
- - **Solution**: Verify secrets are set correctly in Space Settings
111
-
112
- **Problem**: Slow responses
113
- - **Solution**: Consider upgrading to a better hardware tier
114
-
115
- **Problem**: "No relevant sources found"
116
- - **Solution**: Verify your Qdrant instance is accessible and contains data
117
-
118
- ## πŸ“Š Estimated Costs
119
-
120
- - **HF Space (CPU basic)**: Free
121
- - **OpenAI API**: ~$0.01-0.05 per query (GPT-4o-mini)
122
- - **Qdrant Cloud**: Free tier supports up to 1GB
123
-
124
- ## πŸ”„ Updating Your Deployed App
125
-
126
- ```bash
127
- # Make changes locally
128
- # Then push updates
129
- cd YOUR_SPACE_NAME
130
- git add .
131
- git commit -m "Update: description of changes"
132
- git push
133
- ```
134
-
135
- ## πŸ“ Notes
136
-
137
- - The app will save `validation_results.json` during runtime (this is fine, stored in Space's temporary storage)
138
- - Secrets in HF Spaces are injected as environment variables (compatible with your code)
139
- - The `.env` file is only for local development
140
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
QUICK_START.md DELETED
@@ -1,86 +0,0 @@
1
- # πŸš€ Quick Start: Deploy to Hugging Face Spaces
2
-
3
- ## TL;DR - 5 Minute Deploy
4
-
5
- ### Step 1: Prepare Files (Already Done! βœ“)
6
- ```bash
7
- ./prepare_deployment.sh
8
- ```
9
-
10
- ### Step 2: Create HF Space
11
- 1. Go to https://huggingface.co/spaces
12
- 2. Click **"Create new Space"**
13
- 3. Settings:
14
- - Space name: `80k-career-advisor` (or your choice)
15
- - SDK: **Gradio**
16
- - Hardware: **CPU basic** (free)
17
- - Visibility: Public or Private
18
- 4. Click **"Create Space"**
19
-
20
- ### Step 3: Add Secrets (CRITICAL!)
21
- On your Space page β†’ **Settings** β†’ **Variables and Secrets**:
22
-
23
- | Name | Value |
24
- |------|-------|
25
- | `QDRANT_URL` | Your Qdrant instance URL |
26
- | `QDRANT_API_KEY` | Your Qdrant API key |
27
- | `OPENAI_API_KEY` | Your OpenAI API key |
28
-
29
- ### Step 4: Upload Files
30
-
31
- **Easy Way (Web Upload):**
32
- 1. Go to **Files and versions** tab
33
- 2. Click **"Upload files"**
34
- 3. Drag these 6 files from `hf_spaces_deploy/`:
35
- - app.py
36
- - rag_chat.py
37
- - citation_validator.py
38
- - config.py
39
- - requirements.txt
40
- - README.md
41
- 4. Click **"Commit changes to main"**
42
-
43
- **Git Way:**
44
- ```bash
45
- # Clone your new space
46
- git clone https://huggingface.co/spaces/YOUR_USERNAME/YOUR_SPACE_NAME
47
- cd YOUR_SPACE_NAME
48
-
49
- # Copy files
50
- cp ../80k_rag/hf_spaces_deploy/* .
51
-
52
- # Push
53
- git add .
54
- git commit -m "Initial deployment"
55
- git push
56
- ```
57
-
58
- ### Step 5: Wait & Test
59
- - Build takes 2-5 minutes
60
- - Your app will be live at: `https://huggingface.co/spaces/YOUR_USERNAME/YOUR_SPACE_NAME`
61
- - Test with example questions!
62
-
63
- ## Troubleshooting
64
-
65
- | Problem | Solution |
66
- |---------|----------|
67
- | Build fails | Check build logs, verify requirements.txt |
68
- | "Module not found" | Ensure all dependencies in requirements.txt |
69
- | No API response | Verify secrets are set correctly |
70
- | "No relevant sources" | Check Qdrant instance is accessible |
71
-
72
- ## Cost
73
-
74
- - **HF Space**: FREE (CPU basic tier)
75
- - **OpenAI**: ~$0.01-0.05 per query
76
- - **Qdrant**: FREE (up to 1GB)
77
-
78
- Total: Essentially free for moderate usage!
79
-
80
- ## Need Help?
81
-
82
- See detailed guides:
83
- - `HF_SPACES_CHECKLIST.md` - Complete checklist
84
- - `DEPLOYMENT.md` - Detailed deployment guide
85
- - `README.md` - Full project documentation
86
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hf_spaces_deploy/README.md DELETED
@@ -1,102 +0,0 @@
1
- ---
2
- title: 80,000 Hours RAG Q&A
3
- emoji: 🎯
4
- colorFrom: blue
5
- colorTo: purple
6
- sdk: gradio
7
- sdk_version: 4.0.0
8
- app_file: app.py
9
- pinned: false
10
- ---
11
-
12
- # 🎯 80,000 Hours Career Advice Q&A
13
-
14
- A Retrieval-Augmented Generation (RAG) system that answers career-related questions using content from [80,000 Hours](https://80000hours.org/), with validated citations.
15
-
16
- ## Features
17
-
18
- - πŸ” **Semantic Search**: Retrieves relevant content from 80,000 Hours articles
19
- - πŸ€– **AI-Powered Answers**: Uses GPT-4o-mini to generate comprehensive responses
20
- - βœ… **Citation Validation**: Automatically validates that quotes exist in source material
21
- - πŸ“š **Source Attribution**: Every answer includes validated citations with URLs
22
-
23
- ## How It Works
24
-
25
- 1. Your question is converted to a vector embedding
26
- 2. Relevant article chunks are retrieved from Qdrant vector database
27
- 3. GPT-4o-mini generates an answer with citations
28
- 4. Citations are validated against source material
29
- 5. You get an answer with verified quotes and source links
30
-
31
- ## Configuration for Hugging Face Spaces
32
-
33
- To deploy this app, you need to configure the following **Secrets** in your Space settings:
34
-
35
- 1. Go to your Space β†’ Settings β†’ Variables and Secrets
36
- 2. Add these secrets:
37
- - `QDRANT_URL`: Your Qdrant cloud instance URL
38
- - `QDRANT_API_KEY`: Your Qdrant API key
39
- - `OPENAI_API_KEY`: Your OpenAI API key
40
-
41
- ## Local Development
42
-
43
- ### Setup
44
-
45
- 1. Install dependencies:
46
- ```bash
47
- pip install -r requirements.txt
48
- ```
49
-
50
- 2. Create `.env` file with:
51
- ```
52
- QDRANT_URL=your_url
53
- QDRANT_API_KEY=your_key
54
- OPENAI_API_KEY=your_key
55
- ```
56
-
57
- ### First Time Setup (run in order):
58
-
59
- 1. **Extract articles** β†’ `python extract_articles_cli.py`
60
- - Scrapes 80,000 Hours articles from sitemap
61
- - Only needed once (or to refresh content)
62
-
63
- 2. **Chunk articles** β†’ `python chunk_articles_cli.py`
64
- - Splits articles into semantic chunks
65
-
66
- 3. **Upload to Qdrant** β†’ `python upload_to_qdrant_cli.py`
67
- - Generates embeddings and uploads to vector DB
68
-
69
- ### Running Locally
70
-
71
- **Web Interface:**
72
- ```bash
73
- python app.py
74
- ```
75
-
76
- **Command Line:**
77
- ```bash
78
- python rag_chat.py "your question here"
79
- python rag_chat.py "your question" --show-context
80
- ```
81
-
82
- ## Project Structure
83
-
84
- - `app.py` - Main Gradio web interface
85
- - `rag_chat.py` - RAG logic and CLI interface
86
- - `citation_validator.py` - Citation validation system
87
- - `extract_articles_cli.py` - Article scraper
88
- - `chunk_articles_cli.py` - Article chunking
89
- - `upload_to_qdrant_cli.py` - Vector DB uploader
90
- - `config.py` - Shared configuration
91
-
92
- ## Tech Stack
93
-
94
- - **Frontend**: Gradio 4.0+
95
- - **LLM**: OpenAI GPT-4o-mini
96
- - **Vector DB**: Qdrant Cloud
97
- - **Embeddings**: sentence-transformers (all-MiniLM-L6-v2)
98
- - **Citation Validation**: rapidfuzz for fuzzy matching
99
-
100
- ## Credits
101
-
102
- Content sourced from [80,000 Hours](https://80000hours.org/), a nonprofit that provides research and support to help people find careers that effectively tackle the world's most pressing problems.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hf_spaces_deploy/app.py DELETED
@@ -1,102 +0,0 @@
1
- import gradio as gr
2
- import os
3
- from rag_chat import ask
4
-
5
- def chat_interface(question: str, show_context: bool = False):
6
- """Process question and return formatted response."""
7
- if not question.strip():
8
- return "Please enter a question.", ""
9
-
10
- result = ask(question, show_context=show_context)
11
-
12
- # Format main response
13
- answer = result["answer"]
14
-
15
- # Format citations
16
- citations_text = ""
17
- if result["citations"]:
18
- citations_text += "\n\n---\n\n### πŸ“š Citations\n\n"
19
- for i, citation in enumerate(result["citations"], 1):
20
- citations_text += f"**[{i}]** {citation['title']}\n"
21
- citations_text += f"> \"{citation['quote']}\"\n"
22
- citations_text += f"πŸ”— [{citation['url']}]({citation['url']})\n\n"
23
-
24
- # Add validation warnings if any
25
- if result.get("validation_errors"):
26
- citations_text += "\n⚠️ **Validation Warnings:**\n"
27
- for error in result["validation_errors"]:
28
- citations_text += f"- {error}\n"
29
-
30
- # Add stats
31
- if result["citations"]:
32
- valid_count = len([c for c in result["citations"] if c.get("validated", True)])
33
- total_count = len(result["citations"])
34
- citations_text += f"\nβœ“ {valid_count}/{total_count} citations validated"
35
-
36
- return answer, citations_text
37
-
38
- # Create Gradio interface
39
- with gr.Blocks(title="80,000 Hours Q&A", theme=gr.themes.Soft()) as demo:
40
- gr.Markdown(
41
- """
42
- # 🎯 80,000 Hours Career Advice Q&A
43
- Ask questions about career planning and get answers backed by citations from 80,000 Hours articles.
44
-
45
- This RAG system retrieves relevant content from the 80,000 Hours knowledge base and generates answers with validated citations.
46
- """
47
- )
48
-
49
- with gr.Row():
50
- with gr.Column():
51
- question_input = gr.Textbox(
52
- label="Your Question",
53
- placeholder="e.g., Should I plan my entire career?",
54
- lines=2
55
- )
56
- show_context_checkbox = gr.Checkbox(
57
- label="Show retrieved context (for debugging)",
58
- value=False
59
- )
60
- submit_btn = gr.Button("Ask", variant="primary")
61
-
62
- with gr.Row():
63
- with gr.Column():
64
- answer_output = gr.Textbox(
65
- label="Answer",
66
- lines=10,
67
- show_copy_button=True
68
- )
69
-
70
- with gr.Column():
71
- citations_output = gr.Markdown(
72
- label="Citations & Sources"
73
- )
74
-
75
- # Event handlers
76
- submit_btn.click(
77
- fn=chat_interface,
78
- inputs=[question_input, show_context_checkbox],
79
- outputs=[answer_output, citations_output]
80
- )
81
-
82
- question_input.submit(
83
- fn=chat_interface,
84
- inputs=[question_input, show_context_checkbox],
85
- outputs=[answer_output, citations_output]
86
- )
87
-
88
- # Example questions
89
- gr.Examples(
90
- examples=[
91
- "Should I plan my entire career?",
92
- "What career advice does 80k give?",
93
- "How can I have more impact with my career?",
94
- "What are the world's most pressing problems?",
95
- ],
96
- inputs=question_input
97
- )
98
-
99
- if __name__ == "__main__":
100
- # HF Spaces handles the server configuration
101
- demo.launch()
102
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hf_spaces_deploy/citation_validator.py DELETED
@@ -1,338 +0,0 @@
1
- """Citation validation and formatting for RAG system.
2
-
3
- This module handles structured citations with validation to prevent hallucination.
4
- """
5
-
6
- import json
7
- import time
8
- from typing import List, Dict, Any
9
- from urllib.parse import quote
10
- from openai import OpenAI
11
- from rapidfuzz import fuzz
12
-
13
-
14
- FUZZY_THRESHOLD = 95
15
-
16
- def create_highlighted_url(base_url: str, quote_text: str) -> str:
17
- """Create a URL with text fragment that highlights the quoted text.
18
-
19
- Uses the :~:text= URL fragment feature to scroll to and highlight text.
20
-
21
- Args:
22
- base_url: The base article URL
23
- quote_text: The text to highlight
24
-
25
- Returns:
26
- URL with text fragment
27
- """
28
- # Take first ~100 characters of quote for the URL (browsers have limits)
29
- # and clean up for URL encoding
30
- text_fragment = quote_text[:100].strip()
31
- encoded_text = quote(text_fragment)
32
- return f"{base_url}#:~:text={encoded_text}"
33
-
34
-
35
- def normalize_text(text: str) -> str:
36
- """Normalize text for comparison by handling whitespace and punctuation variants."""
37
- # Normalize different dash types to standard hyphen
38
- text = text.replace('–', '-') # en-dash
39
- text = text.replace('β€”', '-') # em-dash
40
- text = text.replace('βˆ’', '-') # minus sign
41
- # Normalize different apostrophe/quote types to standard ASCII
42
- text = text.replace(''', "'") # curly apostrophe
43
- text = text.replace(''', "'") # left single quote
44
- text = text.replace('"', '"') # left double quote
45
- text = text.replace('"', '"') # right double quote
46
- # Normalize whitespace
47
- text = " ".join(text.split())
48
- return text
49
-
50
-
51
- def validate_citation(quote: str, source_chunks: List[Any], source_id: int) -> Dict[str, Any]:
52
- """Validate that a quote exists in the specified source chunk.
53
-
54
- Args:
55
- quote: The quoted text to validate
56
- source_chunks: List of source chunks from Qdrant
57
- source_id: 1-indexed source ID
58
-
59
- Returns:
60
- Dict with validation result and metadata
61
- """
62
- if source_id < 1 or source_id > len(source_chunks):
63
- return {
64
- "valid": False,
65
- "quote": quote,
66
- "source_id": source_id,
67
- "reason": "Invalid source ID",
68
- "source_text": None
69
- }
70
-
71
- quote_clean = normalize_text(quote).lower()
72
-
73
-
74
- # Step 1: Check claimed source first (fast path)
75
- source_text = normalize_text(source_chunks[source_id - 1].payload['text']).lower()
76
- claimed_score = fuzz.partial_ratio(quote_clean, source_text)
77
-
78
- if claimed_score >= FUZZY_THRESHOLD:
79
- return {
80
- "valid": True,
81
- "quote": quote,
82
- "source_id": source_id,
83
- "title": source_chunks[source_id - 1].payload['title'],
84
- "url": source_chunks[source_id - 1].payload['url'],
85
- "similarity_score": claimed_score
86
- }
87
-
88
- for idx, chunk in enumerate(source_chunks, 1):
89
- if idx == source_id:
90
- continue # Already checked
91
- chunk_text = normalize_text(chunk.payload['text']).lower()
92
- score = fuzz.partial_ratio(quote_clean, chunk_text)
93
- if score >= FUZZY_THRESHOLD:
94
- return {
95
- "valid": True,
96
- "quote": quote,
97
- "source_id": idx,
98
- "title": chunk.payload['title'],
99
- "url": chunk.payload['url'],
100
- "similarity_score": score,
101
- "remapped": True,
102
- "original_source_id": source_id
103
- }
104
-
105
- # Validation failed - report best score from claimed source
106
- return {
107
- "valid": False,
108
- "quote": quote,
109
- "source_id": source_id,
110
- "reason": f"Quote not found in any source (claimed source: {claimed_score:.1f}% similarity)",
111
- "source_text": source_chunks[source_id - 1].payload['text']
112
- }
113
-
114
-
115
- def generate_answer_with_citations(
116
- question: str,
117
- context: str,
118
- results: List[Any],
119
- llm_model: str,
120
- openai_api_key: str
121
- ) -> Dict[str, Any]:
122
- """Generate answer with structured citations using OpenAI.
123
-
124
- Args:
125
- question: User's question
126
- context: Formatted context from source chunks
127
- results: Source chunks from Qdrant
128
- llm_model: OpenAI model name
129
- openai_api_key: OpenAI API key
130
-
131
- Returns:
132
- Dict with answer and validated citations
133
- """
134
- client = OpenAI(api_key=openai_api_key)
135
-
136
- system_prompt = """You are a helpful assistant that answers questions based on 80,000 Hours articles.
137
-
138
- You MUST return your response in valid JSON format with this exact structure:
139
- {
140
- "answer": "Your conversational answer with inline citation markers like [1], [2]",
141
- "citations": [
142
- {
143
- "citation_id": 1,
144
- "source_id": 1,
145
- "quote": "exact sentence or sentences from the source that support your claim"
146
- }
147
- ]
148
- }
149
-
150
- CITATION HARD RULES:
151
- 1. Copy quotes EXACTLY as they appear in the provided context
152
- - NO ellipses (...)
153
- - NO paraphrasing
154
- - NO punctuation changes
155
- - Word-for-word, character-for-character accuracy required
156
-
157
- 2. If the needed support is in two places, use TWO SEPARATE citation entries
158
- - Do NOT combine quotes from different sources or different parts of text
159
- - Each citation must contain a continuous, unmodified quote
160
-
161
- 3. Use the CORRECT source_id from the provided list
162
- - Source IDs are numbered [Source 1], [Source 2], etc. in the context
163
- - Verify the source_id matches where you found the quote
164
-
165
- CRITICAL RULES FOR CITATIONS:
166
- - For EVERY claim (advice, fact, statistic, recommendation), add an inline citation [1], [2], etc.
167
- - For each citation, extract and quote the EXACT sentence(s) from the source that directly support your claim
168
- - Find the specific sentence(s) in the source that contain the relevant information
169
- - Each quote should be at least 20 characters and contain complete sentence(s)
170
- - Multiple consecutive sentences can be quoted if needed to fully support the claim
171
-
172
- WRITING STYLE:
173
- - Write concisely in a natural, conversational tone
174
- - You may paraphrase information in your answer, but always cite the source with exact quotes
175
- - You can add brief context/transitions without citations, but cite all substantive claims
176
- - If the sources don't fully answer the question, acknowledge that briefly
177
- - Only use information from the provided sources - don't add external knowledge
178
-
179
- EXAMPLES:
180
-
181
- Example 1 - Single claim:
182
- {
183
- "answer": "One of the most effective ways to build career capital is to work at a high-performing organization where you can learn from talented colleagues [1].",
184
- "citations": [
185
- {
186
- "citation_id": 1,
187
- "source_id": 2,
188
- "quote": "Working at a high-performing organization is one of the fastest ways to build career capital because you learn from talented colleagues and develop strong professional networks."
189
- }
190
- ]
191
- }
192
-
193
- Example 2 - Multiple claims:
194
- {
195
- "answer": "AI safety is considered one of the most pressing problems of our time [1]. Experts estimate that advanced AI could be developed within the next few decades [2], and there's a significant talent gap in the field [3]. This means your contributions could have an outsized impact.",
196
- "citations": [
197
- {
198
- "citation_id": 1,
199
- "source_id": 1,
200
- "quote": "We believe that risks from artificial intelligence are one of the most pressing problems facing humanity today."
201
- },
202
- {
203
- "citation_id": 2,
204
- "source_id": 1,
205
- "quote": "Many AI researchers believe there's a 10-50% chance of human-level AI being developed by 2050."
206
- },
207
- {
208
- "citation_id": 3,
209
- "source_id": 3,
210
- "quote": "There are currently fewer than 300 people working full-time on technical AI safety research, despite the field's critical importance."
211
- }
212
- ]
213
- }"""
214
-
215
- user_prompt = f"""Context from 80,000 Hours articles:
216
-
217
- {context}
218
-
219
- Question: {question}
220
-
221
- Provide your answer in JSON format with exact quotes from the sources."""
222
-
223
- llm_call_start = time.time()
224
- response = client.chat.completions.create(
225
- model=llm_model,
226
- messages=[
227
- {"role": "system", "content": system_prompt},
228
- {"role": "user", "content": user_prompt}
229
- ],
230
- response_format={"type": "json_object"}
231
- )
232
- print(f"[TIMING] OpenAI call: {(time.time() - llm_call_start)*1000:.2f}ms")
233
-
234
- # Parse the JSON response
235
- try:
236
- result = json.loads(response.choices[0].message.content)
237
- # Enforce strict shape: must have 'answer' (str) and 'citations' (list of dicts)
238
- if not isinstance(result, dict) or 'answer' not in result or 'citations' not in result:
239
- return {
240
- "answer": response.choices[0].message.content,
241
- "citations": [],
242
- "validation_errors": ["Response JSON missing required keys 'answer' and/or 'citations'."]
243
- }
244
- if not isinstance(result['answer'], str) or not isinstance(result['citations'], list):
245
- return {
246
- "answer": response.choices[0].message.content,
247
- "citations": [],
248
- "validation_errors": ["Response JSON has incorrect types for 'answer' or 'citations'."]
249
- }
250
- answer = result.get("answer", "")
251
- citations = result.get("citations", [])
252
- except json.JSONDecodeError:
253
- return {
254
- "answer": response.choices[0].message.content,
255
- "citations": [],
256
- "validation_errors": ["Failed to parse JSON response"]
257
- }
258
-
259
- # Validate each citation
260
- validation_start = time.time()
261
- validated_citations = []
262
- validation_errors = []
263
-
264
- for citation in citations:
265
- quote = citation.get("quote", "")
266
- source_id = citation.get("source_id", 0)
267
- citation_id = citation.get("citation_id", 0)
268
-
269
- validation_result = validate_citation(quote, results, source_id)
270
-
271
- if validation_result["valid"]:
272
- # Create URL with text fragment to highlight the quote
273
- highlighted_url = create_highlighted_url(
274
- validation_result["url"],
275
- quote
276
- )
277
- citation_entry = {
278
- "citation_id": citation_id,
279
- "source_id": validation_result["source_id"],
280
- "quote": quote,
281
- "title": validation_result["title"],
282
- "url": highlighted_url,
283
- "similarity_score": validation_result["similarity_score"]
284
- }
285
- if validation_result.get("remapped"):
286
- citation_entry["remapped_from"] = validation_result["original_source_id"]
287
- validated_citations.append(citation_entry)
288
- else:
289
- validation_errors.append({
290
- "citation_id": citation_id,
291
- "reason": validation_result['reason'],
292
- "claimed_quote": quote,
293
- "source_text": validation_result.get('source_text')
294
- })
295
-
296
- print(f"[TIMING] Validation: {(time.time() - validation_start)*1000:.2f}ms")
297
-
298
- return {
299
- "answer": answer,
300
- "citations": validated_citations,
301
- "validation_errors": validation_errors,
302
- "total_citations": len(citations),
303
- "valid_citations": len(validated_citations)
304
- }
305
-
306
-
307
- def format_citations_display(citations: List[Dict[str, Any]]) -> str:
308
- """Format validated citations in order with article title, URL, and quoted text.
309
-
310
- Args:
311
- citations: List of validated citation dicts
312
-
313
- Returns:
314
- Formatted string for display
315
- """
316
- if not citations:
317
- return "No citations available."
318
-
319
- # Sort citations by citation_id to display in order
320
- sorted_citations = sorted(citations, key=lambda x: x.get('citation_id', 0))
321
-
322
- citation_parts = []
323
- for cit in sorted_citations:
324
- marker = f"[{cit['citation_id']}]"
325
- score = cit.get('similarity_score', 100)
326
-
327
- if cit.get('remapped_from'):
328
- note = f" ({score:.1f}% match, remapped: source {cit['remapped_from']} β†’ {cit['source_id']})"
329
- else:
330
- note = f" ({score:.1f}% match)"
331
-
332
- citation_parts.append(
333
- f"{marker} {cit['title']}{note}\n"
334
- f" URL: {cit['url']}\n"
335
- f" Quote: \"{cit['quote']}\"\n"
336
- )
337
- return "\n".join(citation_parts)
338
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hf_spaces_deploy/config.py DELETED
@@ -1,11 +0,0 @@
1
- """Shared configuration constants for the 80k RAG system."""
2
-
3
- # Embedding model used across the system
4
- MODEL_NAME = 'all-MiniLM-L6-v2'
5
-
6
- # Qdrant collection name
7
- COLLECTION_NAME = "80k_articles"
8
-
9
- # Embedding dimension for the model
10
- EMBEDDING_DIM = 384
11
-
 
 
 
 
 
 
 
 
 
 
 
 
hf_spaces_deploy/rag_chat.py DELETED
@@ -1,181 +0,0 @@
1
- import os
2
- import time
3
- from typing import Dict, Any
4
- from dotenv import load_dotenv
5
- from qdrant_client import QdrantClient
6
- from sentence_transformers import SentenceTransformer
7
- from citation_validator import generate_answer_with_citations, format_citations_display, normalize_text
8
- from config import MODEL_NAME, COLLECTION_NAME
9
-
10
- load_dotenv()
11
-
12
- LLM_MODEL = "gpt-4o-mini"
13
- SOURCE_COUNT = 10
14
- SCORE_THRESHOLD = 0.4
15
-
16
- def retrieve_context(question):
17
- """Retrieve relevant chunks from Qdrant."""
18
- start = time.time()
19
-
20
- client = QdrantClient(
21
- url=os.getenv("QDRANT_URL"),
22
- api_key=os.getenv("QDRANT_API_KEY"),
23
- )
24
-
25
- model = SentenceTransformer(MODEL_NAME)
26
- query_vector = model.encode(question).tolist()
27
-
28
- results = client.query_points(
29
- collection_name=COLLECTION_NAME,
30
- query=query_vector,
31
- limit=SOURCE_COUNT,
32
- score_threshold=SCORE_THRESHOLD,
33
- )
34
- print(f"[TIMING] Retrieval: {(time.time() - start)*1000:.2f}ms")
35
-
36
- return results.points
37
-
38
- def format_context(results):
39
- """Format retrieved chunks into context string for LLM."""
40
- context_parts = []
41
- for i, hit in enumerate(results, 1):
42
- context_parts.append(
43
- f"[Source {i}]\n"
44
- f"Title: {hit.payload['title']}\n"
45
- f"URL: {hit.payload['url']}\n"
46
- f"Content: {hit.payload['text']}\n"
47
- )
48
- return "\n---\n".join(context_parts)
49
-
50
- def ask(question: str, show_context: bool = False) -> Dict[str, Any]:
51
- """Main RAG function: retrieve context and generate answer with validated citations."""
52
- total_start = time.time()
53
- print(f"Question: {question}\n")
54
-
55
- # Retrieve relevant chunks
56
- results = retrieve_context(question)
57
-
58
- if not results:
59
- print("No relevant sources found above the score threshold.")
60
- return {
61
- "question": question,
62
- "answer": "No relevant information found in the knowledge base.",
63
- "citations": [],
64
- "sources": []
65
- }
66
-
67
- context = format_context(results)
68
- print(f"[TIMING] First chunk ready: {(time.time() - total_start)*1000:.2f}ms")
69
-
70
- if show_context:
71
- print("=" * 80)
72
- print("RETRIEVED CONTEXT:")
73
- print("=" * 80)
74
- print(context)
75
- print("\n")
76
-
77
- # Generate answer with citations
78
- llm_start = time.time()
79
- result = generate_answer_with_citations(
80
- question=question,
81
- context=context,
82
- results=results,
83
- llm_model=LLM_MODEL,
84
- openai_api_key=os.getenv("OPENAI_API_KEY")
85
- )
86
-
87
- total_time = (time.time() - total_start) * 1000
88
- print(f"[TIMING] Total: {total_time:.2f}ms ({total_time/1000:.2f}s)")
89
-
90
- # Display answer
91
- print("\n" + "=" * 80)
92
- print("ANSWER:")
93
- print("=" * 80)
94
- print(result["answer"])
95
- print("\n")
96
-
97
- # Display citations
98
- print("=" * 80)
99
- print("CITATIONS (Verified Quotes):")
100
- print("=" * 80)
101
- print(format_citations_display(result["citations"]))
102
-
103
- # Show validation stats
104
- if result["validation_errors"]:
105
- print("\n" + "=" * 80)
106
- print("VALIDATION WARNINGS:")
107
- print("=" * 80)
108
- for error in result["validation_errors"]:
109
- print(f"⚠ [Citation {error['citation_id']}] {error['reason']}")
110
-
111
- print("\n" + "=" * 80)
112
- print(f"Citation Stats: {result['valid_citations']}/{result['total_citations']} citations validated")
113
- print("=" * 80)
114
-
115
- # Save validation results to JSON
116
- def normalize_dict(obj):
117
- """Recursively normalize all strings in a dict/list structure."""
118
- if isinstance(obj, dict):
119
- return {k: normalize_dict(v) for k, v in obj.items()}
120
- elif isinstance(obj, list):
121
- return [normalize_dict(item) for item in obj]
122
- elif isinstance(obj, str):
123
- return normalize_text(obj)
124
- return obj
125
-
126
- validation_output = {
127
- "question": question,
128
- "answer": result["answer"],
129
- "citations": result["citations"],
130
- "validation_errors": result["validation_errors"],
131
- "stats": {
132
- "total_citations": result["total_citations"],
133
- "valid_citations": result["valid_citations"],
134
- "total_time_ms": total_time
135
- },
136
- "sources": [
137
- {
138
- "source_id": i,
139
- "title": hit.payload['title'],
140
- "url": hit.payload['url'],
141
- "chunk_id": hit.payload.get('chunk_id'),
142
- "text": hit.payload['text']
143
- }
144
- for i, hit in enumerate(results, 1)
145
- ]
146
- }
147
-
148
- # Normalize all text in the output
149
- validation_output = normalize_dict(validation_output)
150
-
151
- import json
152
- with open("validation_results.json", "w", encoding="utf-8") as f:
153
- json.dump(validation_output, f, ensure_ascii=False, indent=2)
154
- print("\n[INFO] Validation results saved to validation_results.json")
155
-
156
- return {
157
- "question": question,
158
- "answer": result["answer"],
159
- "citations": result["citations"],
160
- "validation_errors": result["validation_errors"],
161
- "sources": results
162
- }
163
-
164
- def main():
165
- import sys
166
-
167
- # Default test query if no args provided
168
- if len(sys.argv) < 2:
169
- question = "Should I plan my entire career?"
170
- show_context = False
171
- print(f"[INFO] No query provided, using test query: '{question}'\n")
172
- else:
173
- show_context = "--show-context" in sys.argv
174
- question_parts = [arg for arg in sys.argv[1:] if arg != "--show-context"]
175
- question = " ".join(question_parts)
176
-
177
- ask(question, show_context=show_context)
178
-
179
- if __name__ == "__main__":
180
- main()
181
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
hf_spaces_deploy/requirements.txt DELETED
@@ -1,10 +0,0 @@
1
- openai>=1.0.0
2
- qdrant-client>=1.7.0
3
- sentence-transformers>=2.2.0
4
- python-dotenv>=1.0.0
5
- beautifulsoup4>=4.12.0
6
- requests>=2.31.0
7
- gradio>=4.0.0
8
- rapidfuzz>=3.0.0
9
- torch>=2.0.0
10
-
 
 
 
 
 
 
 
 
 
 
 
prepare_deployment.sh DELETED
@@ -1,34 +0,0 @@
1
- #!/bin/bash
2
- # Helper script to prepare files for Hugging Face Spaces deployment
3
-
4
- echo "πŸ“¦ Preparing files for Hugging Face Spaces deployment..."
5
- echo ""
6
-
7
- # Create deployment directory
8
- DEPLOY_DIR="hf_spaces_deploy"
9
- rm -rf $DEPLOY_DIR
10
- mkdir -p $DEPLOY_DIR
11
-
12
- # Copy necessary files
13
- echo "Copying files..."
14
- cp app.py $DEPLOY_DIR/
15
- cp rag_chat.py $DEPLOY_DIR/
16
- cp citation_validator.py $DEPLOY_DIR/
17
- cp config.py $DEPLOY_DIR/
18
- cp requirements.txt $DEPLOY_DIR/
19
- cp README.md $DEPLOY_DIR/
20
-
21
- echo "βœ… Files copied to $DEPLOY_DIR/"
22
- echo ""
23
- echo "Files ready for deployment:"
24
- ls -lh $DEPLOY_DIR/
25
- echo ""
26
- echo "πŸ“‹ Next steps:"
27
- echo "1. Create your Hugging Face Space at https://huggingface.co/spaces"
28
- echo "2. Configure secrets (QDRANT_URL, QDRANT_API_KEY, OPENAI_API_KEY)"
29
- echo "3. Clone your space: git clone https://huggingface.co/spaces/YOUR_USERNAME/YOUR_SPACE"
30
- echo "4. Copy files: cp hf_spaces_deploy/* YOUR_SPACE/"
31
- echo "5. Push: cd YOUR_SPACE && git add . && git commit -m 'Initial deployment' && git push"
32
- echo ""
33
- echo "πŸ“– See HF_SPACES_CHECKLIST.md for detailed instructions"
34
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
upload_to_qdrant_cli.py CHANGED
@@ -29,7 +29,11 @@ def load_embedding_model():
29
  def create_collection(client, collection_name=COLLECTION_NAME, embedding_dim=EMBEDDING_DIM):
30
  print(f"Creating collection '{collection_name}'...")
31
  try:
32
- client.recreate_collection(
 
 
 
 
33
  collection_name=collection_name,
34
  vectors_config=VectorParams(size=embedding_dim, distance=Distance.COSINE),
35
  )
 
29
  def create_collection(client, collection_name=COLLECTION_NAME, embedding_dim=EMBEDDING_DIM):
30
  print(f"Creating collection '{collection_name}'...")
31
  try:
32
+ if client.collection_exists(collection_name):
33
+ print(f"Collection '{collection_name}' exists. Deleting...")
34
+ client.delete_collection(collection_name)
35
+
36
+ client.create_collection(
37
  collection_name=collection_name,
38
  vectors_config=VectorParams(size=embedding_dim, distance=Distance.COSINE),
39
  )