rizzdeep commited on
Commit
9a28e83
Β·
verified Β·
1 Parent(s): 6309ec6

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +70 -284
app.py CHANGED
@@ -1,23 +1,71 @@
1
  # ---- Standard libs ----
2
  import os, tempfile
3
  from datetime import datetime
4
- import json
5
 
6
  # ---- Streamlit ----
7
  import streamlit as st
8
 
9
  # ---- Load environment variables (.env) ----
10
  from dotenv import load_dotenv
11
- load_dotenv()
12
 
13
  # ---- RAG + Groq stack ----
14
  from langchain_groq import ChatGroq
15
  from langchain.embeddings import HuggingFaceEmbeddings
16
  from langchain_community.vectorstores import Chroma
17
  from langchain.chains import RetrievalQA
18
- from langchain_community.document_loaders import PyPDFLoader
19
  from langchain.text_splitter import RecursiveCharacterTextSplitter
20
- from langchain_chroma import Chroma
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  # ------------- Configuration -------------
22
  st.set_page_config(
23
  page_title="πŸ“„ Multimodal Chat Assistant",
@@ -170,294 +218,32 @@ st.markdown("""
170
  margin-bottom: 2rem;
171
  }
172
  </style>
173
- """, unsafe_allow_html=True)
174
-
175
- # ------------- One‑time chain initializer -------------
176
- @st.cache_resource(show_spinner=False)
177
- def init_rag_chain(model_name="llama3-70b-8192", temperature=0.2):
178
- embedding = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")
179
- vectordb = Chroma(
180
- persist_directory="./rag_db",
181
- embedding_function=embedding,
182
- collection_metadata={"hnsw:space": "cosine"}
183
- )
184
- llm = ChatGroq(
185
- groq_api_key=os.getenv("GROQ_API_KEY"),
186
- model=model_name,
187
- temperature=temperature,
188
- )
189
- return RetrievalQA.from_chain_type(
190
- llm=llm,
191
- retriever=vectordb.as_retriever(search_kwargs={"k": 4}),
192
- return_source_documents=False
193
- )
194
-
195
- # ------------- Document ingestion -------------
196
- # updated import βœ…
197
-
198
- def ingest_pdf(file_bytes: bytes, file_name: str):
199
- embedding = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")
200
 
201
- vectordb = Chroma(
202
- persist_directory="./rag_db",
203
- embedding_function=embedding,
204
- collection_metadata={"hnsw:space": "cosine"}
205
- )
206
-
207
- with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as tmp:
208
- tmp.write(file_bytes)
209
- tmp_path = tmp.name
210
-
211
- loader = PyPDFLoader(tmp_path)
212
- docs = loader.load()
213
-
214
- splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
215
- chunks = splitter.split_documents(docs)
216
-
217
- os.unlink(tmp_path)
218
-
219
- if not chunks:
220
- st.error("❌ No text chunks extracted from the PDF.")
221
- return 0
222
-
223
- # Remove empty chunks
224
- non_empty_chunks = [chunk for chunk in chunks if chunk.page_content.strip()]
225
- if not non_empty_chunks:
226
- st.error("❌ All extracted chunks are empty. Check your PDF content.")
227
- return 0
228
-
229
- vectordb.add_documents(non_empty_chunks)
230
- vectordb.persist()
231
-
232
- if "documents" not in st.session_state:
233
- st.session_state.documents = []
234
-
235
- st.session_state.documents.append({
236
- "name": file_name,
237
- "chunks": len(non_empty_chunks),
238
- "uploaded_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
239
- })
240
-
241
- return len(non_empty_chunks)
242
-
243
-
244
- # ------------- Initialize session state -------------
245
- if "chat_history" not in st.session_state:
246
- st.session_state.chat_history = []
247
- if "documents" not in st.session_state:
248
- st.session_state.documents = []
249
- if "total_questions" not in st.session_state:
250
- st.session_state.total_questions = 0
251
-
252
-
253
- # ------------- Sidebar Configuration -------------
254
- with st.sidebar:
255
- st.markdown("### βš™οΈ Configuration")
256
-
257
- # Model selection
258
- model_options = {
259
- "Llama 3 70B": "llama3-70b-8192",
260
- "Llama 3 8B": "llama3-8b-8192",
261
- "Mixtral 8x7B": "mixtral-8x7b-32768",
262
- "Gemma 7B": "gemma-7b-it"
263
- }
264
-
265
- selected_model = st.selectbox(
266
- "πŸ€– Select Model",
267
- options=list(model_options.keys()),
268
- index=0
269
- )
270
-
271
- temperature = st.slider(
272
- "🌑️ Temperature",
273
- min_value=0.0,
274
- max_value=1.0,
275
- value=0.2,
276
- step=0.1,
277
- help="Higher values make output more creative"
278
- )
279
-
280
- # Document statistics
281
- st.markdown("### πŸ“Š Statistics")
282
-
283
- col1, col2 = st.columns(2)
284
- with col1:
285
- st.markdown(f"""
286
- <div class="stats-card">
287
- <h4>πŸ“„ Documents</h4>
288
- <h2>{len(st.session_state.documents)}</h2>
289
- </div>
290
- """, unsafe_allow_html=True)
291
-
292
- with col2:
293
- st.markdown(f"""
294
- <div class="stats-card">
295
- <h4>πŸ’¬ Questions</h4>
296
- <h2>{st.session_state.total_questions}</h2>
297
- </div>
298
- """, unsafe_allow_html=True)
299
-
300
- # Document list
301
- if st.session_state.documents:
302
- st.markdown("### πŸ“š Uploaded Documents")
303
- for doc in st.session_state.documents:
304
- st.markdown(f"""
305
- <div class="feature-card">
306
- <strong>{doc['name']}</strong><br>
307
- <small>πŸ“„ {doc['chunks']} chunks</small><br>
308
- <small>πŸ•’ {doc['uploaded_at']}</small>
309
- </div>
310
- """, unsafe_allow_html=True)
311
-
312
- # Clear chat button
313
- if st.button("πŸ—‘οΈ Clear Chat History", use_container_width=True):
314
- st.session_state.chat_history = []
315
- st.rerun()
316
-
317
- # ------------- Main Interface -------------
318
- st.markdown('<h1 class="premium-title">πŸ€– Multimodal Chat Assistant</h1>', unsafe_allow_html=True)
319
- st.markdown('<p class="subtitle">Upload your documents and chat with AI-powered intelligence</p>', unsafe_allow_html=True)
320
-
321
- # Initialize QA chain with selected model
322
- qa_chain = init_rag_chain(model_options[selected_model], temperature)
323
 
324
  # Upload section
325
- col1, col2, col3 = st.columns([1, 2, 1])
326
- with col2:
327
- st.markdown('<div class="upload-area">', unsafe_allow_html=True)
328
- uploaded_file = st.file_uploader(
329
- "πŸ“€ Upload a PDF file",
330
- type=["pdf"],
331
- help="Upload a PDF document to start chatting with it"
332
- )
333
- st.markdown('</div>', unsafe_allow_html=True)
334
-
335
  if uploaded_file is not None:
336
- with st.spinner("πŸ“š Processing document... This may take a moment."):
337
- progress_bar = st.progress(0)
338
- for i in range(100):
339
- progress_bar.progress(i + 1)
340
-
341
- chunks_count = ingest_pdf(uploaded_file.read(), uploaded_file.name)
342
- progress_bar.empty()
343
-
344
- st.success(f"βœ… Document processed successfully! Created {chunks_count} chunks.")
345
- st.balloons()
346
 
347
- # Chat interface
348
- st.markdown('<div class="chat-container">', unsafe_allow_html=True)
 
349
 
350
- # Suggested questions
351
- if not st.session_state.chat_history and st.session_state.documents:
352
- st.markdown("### πŸ’‘ Suggested Questions")
353
- suggestions = [
354
- "πŸ“‹ What is the main topic of this document?",
355
- "πŸ” Can you summarize the key points?",
356
- "πŸ“Š What are the important statistics mentioned?",
357
- "🎯 What conclusions does the document draw?"
358
- ]
359
-
360
- cols = st.columns(2)
361
- for i, suggestion in enumerate(suggestions):
362
- with cols[i % 2]:
363
- if st.button(suggestion, key=f"suggestion_{i}", use_container_width=True):
364
- st.session_state.chat_history.append({
365
- "role": "user",
366
- "content": suggestion.split(" ", 1)[1],
367
- "timestamp": datetime.now().strftime("%H:%M")
368
- })
369
- st.session_state.total_questions += 1
370
- st.rerun()
371
-
372
- # Chat input
373
- prompt = st.text_input(
374
- "πŸ’¬ Ask a question about your document",
375
- placeholder="Type your question here...",
376
- key="chat_input"
377
- )
378
 
379
  if prompt:
380
- # Add user message
381
- st.session_state.chat_history.append({
382
- "role": "user",
383
- "content": prompt,
384
- "timestamp": datetime.now().strftime("%H:%M")
385
- })
386
- st.session_state.total_questions += 1
387
-
388
- # Generate response
389
  with st.spinner("πŸ€” Thinking..."):
390
- try:
391
- response = qa_chain.run(prompt)
392
- st.session_state.chat_history.append({
393
- "role": "assistant",
394
- "content": response,
395
- "timestamp": datetime.now().strftime("%H:%M")
396
- })
397
- except Exception as e:
398
- st.error(f"❌ Error: {str(e)}")
399
-
400
- st.rerun()
401
 
402
  # Display chat history
403
- if st.session_state.chat_history:
404
- st.markdown("### πŸ’¬ Conversation")
405
-
406
- for i, msg in enumerate(st.session_state.chat_history):
407
- if msg["role"] == "user":
408
- st.markdown(f"""
409
- <div style="display: flex; justify-content: flex-end; margin: 1rem 0;">
410
- <div class="message-user">
411
- <strong>πŸ§‘ You</strong> <small style="opacity: 0.7;">({msg.get('timestamp', '')})</small><br>
412
- {msg['content']}
413
- </div>
414
- </div>
415
- """, unsafe_allow_html=True)
416
- else:
417
- st.markdown(f"""
418
- <div style="display: flex; justify-content: flex-start; margin: 1rem 0;">
419
- <div class="message-bot">
420
- <strong>πŸ€– Assistant</strong> <small style="opacity: 0.7;">({msg.get('timestamp', '')})</small><br>
421
- {msg['content']}
422
- </div>
423
- </div>
424
- """, unsafe_allow_html=True)
425
-
426
- # Add action buttons for bot responses
427
- col1, col2, col3, col4 = st.columns([1, 1, 1, 5])
428
- with col1:
429
- if st.button("πŸ‘", key=f"like_{i}", help="Like this response"):
430
- st.toast("πŸ‘ Response liked!")
431
- with col2:
432
- if st.button("πŸ‘Ž", key=f"dislike_{i}", help="Dislike this response"):
433
- st.toast("πŸ‘Ž Feedback noted!")
434
- with col3:
435
- if st.button("πŸ“‹", key=f"copy_{i}", help="Copy response"):
436
- st.toast("πŸ“‹ Response copied!")
437
-
438
- else:
439
- if not st.session_state.documents:
440
- st.markdown("""
441
- <div class="feature-card" style="text-align: center;">
442
- <h3>πŸš€ Get Started</h3>
443
- <p>Upload a PDF document above to begin chatting with your AI assistant!</p>
444
-
445
- <h4>✨ Features:</h4>
446
- <ul style="text-align: left; display: inline-block;">
447
- <li>πŸ€– Multiple AI models to choose from</li>
448
- <li>πŸ“„ Advanced document processing</li>
449
- <li>πŸ’¬ Interactive chat interface</li>
450
- <li>πŸ“Š Real-time statistics</li>
451
- <li>🎨 Premium UI/UX design</li>
452
- </ul>
453
- </div>
454
- """, unsafe_allow_html=True)
455
-
456
- st.markdown('</div>', unsafe_allow_html=True)
457
-
458
- # Footer
459
- st.markdown("""
460
- <div style="text-align: center; margin-top: 3rem; padding: 2rem; color: rgba(255,255,255,0.6);">
461
- <p>πŸ€– Powered by Groq AI β€’ Built with ❀️ using Streamlit</p>
462
- </div>
463
- """, unsafe_allow_html=True)
 
1
  # ---- Standard libs ----
2
  import os, tempfile
3
  from datetime import datetime
 
4
 
5
  # ---- Streamlit ----
6
  import streamlit as st
7
 
8
  # ---- Load environment variables (.env) ----
9
  from dotenv import load_dotenv
10
+ load_dotenv() # <-- makes GROQ_API_KEY available
11
 
12
  # ---- RAG + Groq stack ----
13
  from langchain_groq import ChatGroq
14
  from langchain.embeddings import HuggingFaceEmbeddings
15
  from langchain_community.vectorstores import Chroma
16
  from langchain.chains import RetrievalQA
17
+ from langchain_community.document_loaders import UnstructuredFileLoader
18
  from langchain.text_splitter import RecursiveCharacterTextSplitter
19
+
20
+
21
+ # ------------- One‑time chain initialiser -------------
22
+ @st.cache_resource(show_spinner=False)
23
+ def init_rag_chain():
24
+ # 1️⃣ Embeddings
25
+ embedding = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")
26
+
27
+ # 2️⃣ Persisted vector DB
28
+ vectordb = Chroma(
29
+ persist_directory="./rag_db",
30
+ embedding_function=embedding
31
+ )
32
+
33
+
34
+
35
+ llm = ChatGroq(
36
+ groq_api_key=os.getenv("GROQ_API_KEY"), # <--- THIS is critical
37
+ model="llama3-70b-8192",
38
+ temperature=0.2,
39
+ )
40
+
41
+
42
+ # 4️⃣ Retrieval‑augmented QA chain
43
+ return RetrievalQA.from_chain_type(
44
+ llm=llm,
45
+ retriever=vectordb.as_retriever(search_kwargs={"k": 4}),
46
+ return_source_documents=False
47
+ )
48
+
49
+ qa_chain = init_rag_chain()
50
+
51
+ # ------------- Optional: live‑ingest a PDF the user uploads -------------
52
+ def ingest_pdf(file_bytes: bytes, file_name: str):
53
+ embedding = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")
54
+ vectordb = Chroma(
55
+ persist_directory="./rag_db",
56
+ embedding_function=embedding
57
+ )
58
+
59
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as tmp:
60
+ tmp.write(file_bytes)
61
+ tmp_path = tmp.name
62
+
63
+ docs = UnstructuredFileLoader(tmp_path).load()
64
+ splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
65
+ vectordb.add_documents(splitter.split_documents(docs))
66
+ vectordb.persist()
67
+
68
+
69
  # ------------- Configuration -------------
70
  st.set_page_config(
71
  page_title="πŸ“„ Multimodal Chat Assistant",
 
218
  margin-bottom: 2rem;
219
  }
220
  </style>
221
+ """, unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
222
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
223
 
224
  # Upload section
225
+ uploaded_file = st.file_uploader("πŸ“€ Upload a PDF file", type=["pdf"])
 
 
 
 
 
 
 
 
 
226
  if uploaded_file is not None:
227
+ with st.spinner("πŸ“š Ingesting document..."):
228
+ ingest_pdf(uploaded_file.read(), uploaded_file.name)
229
+ st.success("βœ… Document ingested. Ask away!")
 
 
 
 
 
 
 
230
 
231
+ # Chat UI
232
+ if "chat_history" not in st.session_state:
233
+ st.session_state.chat_history = []
234
 
235
+ prompt = st.text_input("πŸ’¬ Ask a question about the document")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
236
 
237
  if prompt:
238
+ st.session_state.chat_history.append({"role": "user", "content": prompt})
239
+
 
 
 
 
 
 
 
240
  with st.spinner("πŸ€” Thinking..."):
241
+ response = qa_chain.run(prompt)
242
+ st.session_state.chat_history.append({"role": "assistant", "content": response})
 
 
 
 
 
 
 
 
 
243
 
244
  # Display chat history
245
+ for msg in st.session_state.chat_history:
246
+ if msg["role"] == "user":
247
+ st.markdown(f"<div class='message-bubble'><b>πŸ§‘ You:</b> {msg['content']}</div>", unsafe_allow_html=True)
248
+ else:
249
+ st.markdown(f"<div class='message-bubble bot'><b>πŸ€– Bot:</b> {msg['content']}</div>", unsafe_allow_html=True)