🤖 Smart RAG Assistant
Upload documents and ask questions - powered by Groq & Sentence Transformers
import streamlit as st import os import PyPDF2 import docx from io import BytesIO import numpy as np import pandas as pd from sentence_transformers import SentenceTransformer import faiss import pickle from groq import Groq from typing import List, Tuple import re # Page configuration st.set_page_config( page_title="🤖 Smart RAG Assistant", page_icon="🧠", layout="wide", initial_sidebar_state="expanded" ) # Custom CSS for better styling st.markdown(""" """, unsafe_allow_html=True) class RAGSystem: def __init__(self): self.embedding_model = None self.index = None self.documents = [] self.groq_client = None @st.cache_resource def load_embedding_model(_self): """Load the sentence transformer model""" try: model = SentenceTransformer('all-MiniLM-L6-v2') return model except Exception as e: st.error(f"Error loading embedding model: {str(e)}") return None def setup_groq_client(self, api_key: str): """Setup Groq client""" try: self.groq_client = Groq(api_key=api_key) return True except Exception as e: st.error(f"Error setting up Groq client: {str(e)}") return False def extract_text_from_pdf(self, pdf_file) -> str: """Extract text from PDF file""" try: pdf_reader = PyPDF2.PdfReader(BytesIO(pdf_file.read())) text = "" for page in pdf_reader.pages: text += page.extract_text() + "\n" return text except Exception as e: st.error(f"Error reading PDF: {str(e)}") return "" def extract_text_from_docx(self, docx_file) -> str: """Extract text from DOCX file""" try: doc = docx.Document(BytesIO(docx_file.read())) text = "" for paragraph in doc.paragraphs: text += paragraph.text + "\n" return text except Exception as e: st.error(f"Error reading DOCX: {str(e)}") return "" def chunk_text(self, text: str, chunk_size: int = 500, overlap: int = 50) -> List[str]: """Split text into overlapping chunks""" sentences = re.split(r'[.!?]+', text) chunks = [] current_chunk = "" for sentence in sentences: sentence = sentence.strip() if not sentence: continue if len(current_chunk) + len(sentence) < chunk_size: current_chunk += sentence + ". " else: if current_chunk: chunks.append(current_chunk.strip()) current_chunk = sentence + ". " if current_chunk: chunks.append(current_chunk.strip()) return chunks def create_embeddings_and_index(self, documents: List[str]): """Create embeddings and FAISS index""" if not self.embedding_model: self.embedding_model = self.load_embedding_model() if not self.embedding_model: return False try: # Create embeddings embeddings = self.embedding_model.encode(documents, show_progress_bar=True) # Create FAISS index dimension = embeddings.shape[1] self.index = faiss.IndexFlatIP(dimension) # Inner product similarity # Normalize embeddings for cosine similarity faiss.normalize_L2(embeddings) self.index.add(embeddings.astype('float32')) self.documents = documents return True except Exception as e: st.error(f"Error creating embeddings: {str(e)}") return False def retrieve_relevant_docs(self, query: str, k: int = 3) -> List[Tuple[str, float]]: """Retrieve most relevant documents for the query""" if not self.embedding_model or not self.index: return [] try: # Encode query query_embedding = self.embedding_model.encode([query]) faiss.normalize_L2(query_embedding) # Search scores, indices = self.index.search(query_embedding.astype('float32'), k) results = [] for score, idx in zip(scores[0], indices[0]): if idx < len(self.documents): results.append((self.documents[idx], float(score))) return results except Exception as e: st.error(f"Error retrieving documents: {str(e)}") return [] def generate_answer(self, query: str, context: str, model: str = "llama-3.3-70b-versatile") -> str: """Generate answer using Groq""" if not self.groq_client: return "Error: Groq client not initialized" try: prompt = f"""Based on the following context, please answer the question accurately and concisely. If the answer cannot be found in the context, please say so. Context: {context} Question: {query} Answer:""" chat_completion = self.groq_client.chat.completions.create( messages=[ { "role": "system", "content": "You are a helpful assistant that answers questions based on the provided context. Be accurate and concise." }, { "role": "user", "content": prompt } ], model=model, temperature=0.3, max_tokens=1000 ) return chat_completion.choices[0].message.content except Exception as e: return f"Error generating answer: {str(e)}" def main(): # Header st.markdown("""
Upload documents and ask questions - powered by Groq & Sentence Transformers
Documents: {len(st.session_state.rag_system.documents)} chunks
Status: ✅ Ready
Model: {selected_model}
Status: ❌ No documents loaded
Upload documents to get started!