axelsirota commited on
Commit
d14d397
·
verified ·
1 Parent(s): b182c68

Upload folder using huggingface_hub

Browse files
Files changed (3) hide show
  1. README.md +12 -6
  2. app.py +259 -0
  3. requirements.txt +4 -0
README.md CHANGED
@@ -1,12 +1,18 @@
1
  ---
2
- title: Rag Playground
3
- emoji: 🏢
4
- colorFrom: gray
5
- colorTo: purple
6
  sdk: gradio
7
- sdk_version: 6.5.1
8
  app_file: app.py
9
  pinned: false
 
 
10
  ---
11
 
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
1
  ---
2
+ title: RAG Playground
3
+ emoji: 📚
4
+ colorFrom: green
5
+ colorTo: blue
6
  sdk: gradio
7
+ sdk_version: 5.9.1
8
  app_file: app.py
9
  pinned: false
10
+ license: mit
11
+ short_description: Upload docs, ask questions, see RAG in action
12
  ---
13
 
14
+ # RAG Playground
15
+
16
+ Upload your documents, ask questions, and see how RAG retrieves and generates answers.
17
+
18
+ Part of the **AI for Product Managers** course by Data Trainers LLC.
app.py ADDED
@@ -0,0 +1,259 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import os
3
+ from sentence_transformers import SentenceTransformer
4
+ import chromadb
5
+ from chromadb.config import Settings
6
+ import hashlib
7
+ import re
8
+
9
+ # Initialize embedding model
10
+ model = SentenceTransformer('all-MiniLM-L6-v2')
11
+
12
+ # Initialize ChromaDB
13
+ chroma_client = chromadb.Client(Settings(anonymized_telemetry=False))
14
+
15
+ # Sample documents
16
+ SAMPLE_DOCS = {
17
+ "Support FAQ": """
18
+ Q: What is your return policy?
19
+ A: You can return most items within 30 days of purchase for a full refund. Items must be in original condition with tags attached. Electronics have a 15-day return window.
20
+
21
+ Q: How long does shipping take?
22
+ A: Standard shipping takes 5-7 business days. Express shipping takes 2-3 business days. Free shipping on orders over $50.
23
+
24
+ Q: How do I track my order?
25
+ A: Once your order ships, you'll receive an email with a tracking number. You can also log into your account to view order status.
26
+
27
+ Q: What payment methods do you accept?
28
+ A: We accept Visa, Mastercard, American Express, PayPal, and Apple Pay. All transactions are encrypted and secure.
29
+
30
+ Q: How do I contact customer support?
31
+ A: You can reach us via email at support@example.com, phone at 1-800-EXAMPLE, or live chat on our website. Support hours are 9am-6pm EST.
32
+ """,
33
+ "Product Manual": """
34
+ Product: Smart Home Hub X1
35
+
36
+ Setup Instructions:
37
+ 1. Unbox the device and connect the power adapter
38
+ 2. Download the SmartHome app from your app store
39
+ 3. Create an account or sign in
40
+ 4. Press the pairing button on the hub for 5 seconds until the light blinks blue
41
+ 5. Follow the in-app instructions to complete setup
42
+
43
+ Troubleshooting:
44
+ - If the hub won't connect: Ensure your WiFi is 2.4GHz (not 5GHz). The hub doesn't support 5GHz networks.
45
+ - If lights are unresponsive: Check that the hub firmware is updated in the app settings.
46
+ - If voice commands fail: Verify that your voice assistant is linked in the Integrations menu.
47
+
48
+ Specifications:
49
+ - Dimensions: 4" x 4" x 1.5"
50
+ - Weight: 8 oz
51
+ - Connectivity: WiFi 2.4GHz, Bluetooth 5.0, Zigbee
52
+ - Power: 5V DC, 2A adapter included
53
+ - Compatible with: Alexa, Google Home, Apple HomeKit
54
+
55
+ Warranty: 2-year limited warranty. Register at example.com/warranty within 30 days of purchase.
56
+ """,
57
+ "Company Policy": """
58
+ Remote Work Policy - Effective January 2024
59
+
60
+ Eligibility:
61
+ All full-time employees who have completed their 90-day probation period are eligible for remote work. Certain roles requiring physical presence (e.g., facilities, reception) are exempt.
62
+
63
+ Guidelines:
64
+ - Core hours: All remote employees must be available 10am-3pm in their local timezone for meetings and collaboration.
65
+ - Equipment: The company provides a laptop and monitor. Employees are responsible for reliable internet (minimum 25 Mbps).
66
+ - Workspace: Employees must have a dedicated workspace that allows for video calls without disruption.
67
+
68
+ Expectations:
69
+ - Respond to messages within 2 hours during core hours
70
+ - Attend all scheduled meetings with camera on
71
+ - Complete weekly status updates in the project management tool
72
+ - Be available for occasional in-office days (minimum 2 per month)
73
+
74
+ Expenses:
75
+ - Home office stipend: $500 one-time for setup
76
+ - Internet reimbursement: Up to $50/month
77
+ - Coworking space: Pre-approved expenses reimbursed
78
+
79
+ Violations of this policy may result in revocation of remote work privileges.
80
+ """
81
+ }
82
+
83
+
84
+ def chunk_text(text, chunk_size=500, overlap=100):
85
+ """Split text into overlapping chunks."""
86
+ chunks = []
87
+ start = 0
88
+ while start < len(text):
89
+ end = start + chunk_size
90
+ chunk = text[start:end]
91
+ chunks.append(chunk.strip())
92
+ start = end - overlap
93
+ return [c for c in chunks if c]
94
+
95
+
96
+ def process_documents(doc_text, chunk_size, overlap_pct):
97
+ """Process documents into chunks and store in ChromaDB."""
98
+ overlap = int(chunk_size * overlap_pct / 100)
99
+ chunks = chunk_text(doc_text, chunk_size, overlap)
100
+
101
+ # Create unique collection name
102
+ collection_name = f"docs_{hashlib.md5(doc_text.encode()).hexdigest()[:8]}"
103
+
104
+ # Delete if exists
105
+ try:
106
+ chroma_client.delete_collection(collection_name)
107
+ except:
108
+ pass
109
+
110
+ collection = chroma_client.create_collection(name=collection_name)
111
+
112
+ # Generate embeddings and store
113
+ embeddings = model.encode(chunks).tolist()
114
+ ids = [f"chunk_{i}" for i in range(len(chunks))]
115
+
116
+ collection.add(
117
+ embeddings=embeddings,
118
+ documents=chunks,
119
+ ids=ids
120
+ )
121
+
122
+ return collection, chunks
123
+
124
+
125
+ def query_rag(question, doc_text, chunk_size, overlap_pct, top_k, use_openai):
126
+ """Query the RAG system."""
127
+ if not doc_text.strip():
128
+ return "Please provide documents first.", "", ""
129
+
130
+ # Process documents
131
+ collection, chunks = process_documents(doc_text, chunk_size, overlap_pct)
132
+
133
+ # Embed query
134
+ query_embedding = model.encode([question]).tolist()
135
+
136
+ # Retrieve
137
+ results = collection.query(
138
+ query_embeddings=query_embedding,
139
+ n_results=min(top_k, len(chunks))
140
+ )
141
+
142
+ retrieved_chunks = results['documents'][0]
143
+ distances = results['distances'][0]
144
+
145
+ # Format retrieved context
146
+ context_display = ""
147
+ for i, (chunk, dist) in enumerate(zip(retrieved_chunks, distances)):
148
+ similarity = 1 - dist # Convert distance to similarity
149
+ context_display += f"**Chunk {i+1}** (similarity: {similarity:.2f})\n"
150
+ context_display += f"```\n{chunk}\n```\n\n"
151
+
152
+ # Generate answer
153
+ context = "\n\n".join(retrieved_chunks)
154
+
155
+ if use_openai and os.environ.get("OPENAI_API_KEY"):
156
+ try:
157
+ from openai import OpenAI
158
+ client = OpenAI()
159
+
160
+ prompt = f"""Based on the following context, answer the question. Only use information from the context. If the answer is not in the context, say "I don't have information about that in the provided documents."
161
+
162
+ Context:
163
+ {context}
164
+
165
+ Question: {question}
166
+
167
+ Answer:"""
168
+
169
+ response = client.chat.completions.create(
170
+ model="gpt-4o-mini",
171
+ messages=[{"role": "user", "content": prompt}],
172
+ temperature=0
173
+ )
174
+ answer = response.choices[0].message.content
175
+ except Exception as e:
176
+ answer = f"OpenAI API error: {str(e)}. Using fallback response."
177
+ answer += f"\n\nBased on the retrieved context, the relevant information is:\n{context[:500]}..."
178
+ else:
179
+ # Fallback: simple extractive response
180
+ answer = f"**[Demo Mode - No API Key]**\n\nBased on semantic search, the most relevant information for your question is:\n\n{retrieved_chunks[0]}"
181
+ if len(retrieved_chunks) > 1:
182
+ answer += f"\n\nAdditional relevant context:\n{retrieved_chunks[1][:200]}..."
183
+
184
+ # Groundedness analysis
185
+ groundedness = f"Retrieved {len(retrieved_chunks)} chunks. Top similarity: {1-distances[0]:.2f}"
186
+
187
+ return answer, context_display, groundedness
188
+
189
+
190
+ def load_sample(sample_name):
191
+ """Load a sample document."""
192
+ return SAMPLE_DOCS.get(sample_name, "")
193
+
194
+
195
+ # Build Gradio interface
196
+ with gr.Blocks(title="RAG Playground", theme=gr.themes.Soft()) as demo:
197
+ gr.Markdown(
198
+ "# RAG Playground\n\n"
199
+ "**PM Decision:** Should your team build RAG? Use this to understand what they're "
200
+ "proposing, see where it fails, and estimate costs before committing.\n\n"
201
+ "Upload YOUR documents, ask questions, see how RAG retrieves and generates answers."
202
+ )
203
+
204
+ with gr.Row():
205
+ with gr.Column(scale=1):
206
+ gr.Markdown("### 1. Documents")
207
+ sample_dropdown = gr.Dropdown(
208
+ choices=list(SAMPLE_DOCS.keys()),
209
+ label="Load Sample Document",
210
+ value="Support FAQ"
211
+ )
212
+ doc_input = gr.Textbox(
213
+ label="Document Text",
214
+ placeholder="Paste your document here or select a sample above...",
215
+ lines=10,
216
+ value=SAMPLE_DOCS["Support FAQ"]
217
+ )
218
+
219
+ gr.Markdown("### 2. RAG Configuration")
220
+ chunk_size = gr.Slider(100, 1000, value=500, step=50, label="Chunk Size (characters)")
221
+ overlap = gr.Slider(0, 50, value=20, step=5, label="Overlap (%)")
222
+ top_k = gr.Slider(1, 10, value=3, step=1, label="Chunks to Retrieve")
223
+ use_openai = gr.Checkbox(label="Use OpenAI for generation (requires API key)", value=True)
224
+
225
+ with gr.Column(scale=1):
226
+ gr.Markdown("### 3. Ask a Question")
227
+ question_input = gr.Textbox(
228
+ label="Your Question",
229
+ placeholder="e.g., What is the return policy?",
230
+ lines=2
231
+ )
232
+ query_btn = gr.Button("Ask RAG", variant="primary")
233
+
234
+ gr.Markdown("### 4. Results")
235
+ answer_output = gr.Markdown(label="Generated Answer")
236
+
237
+ with gr.Accordion("Retrieved Chunks", open=False):
238
+ chunks_output = gr.Markdown()
239
+
240
+ groundedness_output = gr.Textbox(label="Retrieval Stats", interactive=False)
241
+
242
+ # Event handlers
243
+ sample_dropdown.change(load_sample, sample_dropdown, doc_input)
244
+
245
+ query_btn.click(
246
+ query_rag,
247
+ inputs=[question_input, doc_input, chunk_size, overlap, top_k, use_openai],
248
+ outputs=[answer_output, chunks_output, groundedness_output]
249
+ )
250
+
251
+ gr.Markdown(
252
+ "---\n"
253
+ "**PM Takeaway:** RAG quality depends on chunking and retrieval - ask your team "
254
+ "how they're handling documents that don't fit neatly into chunks.\n\n"
255
+ "*AI for Product Managers*"
256
+ )
257
+
258
+ if __name__ == "__main__":
259
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ sentence-transformers
2
+ chromadb
3
+ pypdf
4
+ openai