π€ QUADRANT RAG - Document AI Assistant
Powered by Qdrant Vector Database & OpenAI GPT-4o-mini
#!/usr/bin/env python3 """ QUADRANT RAG System - Enhanced UI v2 with Document Library Professional chat interface with persistent document storage """ import os import streamlit as st import json import uuid import time from typing import List, Dict, Any, Optional from pathlib import Path from datetime import datetime, timezone import tempfile import base64 # Load environment variables first import os from dotenv import load_dotenv load_dotenv() # Import RAG components from rag_core import DynamicRAG, extract_pdf_pages, create_chunks # Page configuration st.set_page_config( page_title="QUADRANT RAG - AI Document Assistant", page_icon="π€", layout="wide", initial_sidebar_state="expanded" ) # Enhanced CSS for modern UI st.markdown(""" """, unsafe_allow_html=True) # Initialize session state if 'rag_system' not in st.session_state: st.session_state.rag_system = None if 'current_doc' not in st.session_state: st.session_state.current_doc = None if 'chat_history' not in st.session_state: st.session_state.chat_history = [] if 'all_documents' not in st.session_state: st.session_state.all_documents = [] if 'processing' not in st.session_state: st.session_state.processing = False if 'waiting_for_response' not in st.session_state: st.session_state.waiting_for_response = False def init_rag_system(): """Initialize the RAG system""" try: # Check environment variables from dotenv import load_dotenv load_dotenv() # Reload environment variables openai_key = os.environ.get('OPENAI_API_KEY', '') qdrant_url = os.environ.get('QDRANT_URL', '') qdrant_key = os.environ.get('QDRANT_API_KEY', '') if not openai_key or openai_key == 'your-openai-api-key-here': st.error("β OpenAI API key not configured. Please set OPENAI_API_KEY in your environment.") return False if not qdrant_url or not qdrant_key: st.warning("β οΈ Qdrant Cloud credentials not found. Using local file storage.") # Show initialization progress progress_placeholder = st.empty() with progress_placeholder: with st.spinner("π Initializing RAG System..."): try: st.session_state.rag_system = DynamicRAG() # Load all documents from Qdrant st.session_state.all_documents = st.session_state.rag_system.get_all_documents() except Exception as init_error: st.error(f"β RAG System initialization failed: {str(init_error)}") # Continue anyway for basic functionality st.session_state.all_documents = [] progress_placeholder.success("β RAG System initialized successfully!") return True except Exception as e: st.error(f"β Failed to initialize RAG system: {str(e)}") # Don't fail completely - allow app to show error state return True def process_pdf_upload(uploaded_file) -> Optional[Dict[str, Any]]: """Process uploaded PDF file""" try: st.session_state.processing = True # Save uploaded file temp_path = Path(tempfile.gettempdir()) / f"{uuid.uuid4().hex}.pdf" with open(temp_path, "wb") as f: f.write(uploaded_file.getvalue()) # Extract text pages = extract_pdf_pages(str(temp_path)) # Create chunks chunks = create_chunks(pages, chunk_size=3000, overlap=200) # Generate document ID doc_id = f"{uploaded_file.name.replace('.pdf', '')}_{int(time.time())}" # Store in Qdrant st.session_state.rag_system.store_document(doc_id, chunks) # Create document info doc_info = { 'doc_id': doc_id, 'title': uploaded_file.name, 'pages': len(pages), 'chunks': len(chunks), 'upload_time': datetime.now(timezone.utc).isoformat() } # Update documents list st.session_state.all_documents = st.session_state.rag_system.get_all_documents() # Clean up temp_path.unlink() return doc_info except Exception as e: st.error(f"Error processing PDF: {str(e)}") return None finally: st.session_state.processing = False def query_document(question: str) -> tuple[str, List[Dict[str, Any]]]: """Query the current document""" try: if not st.session_state.current_doc: return "Please select a document first.", [] # Search in current document - increased for better coverage search_results = st.session_state.rag_system.search( query=question, doc_id=st.session_state.current_doc['doc_id'], top_k=10 ) if not search_results: return "I couldn't find relevant information about that in the document.", [] # Generate answer answer = st.session_state.rag_system.generate_answer(question, search_results) # Check if the answer indicates insufficient evidence insufficient_keywords = ["insufficient evidence", "couldn't find", "no relevant information", "cannot answer"] # Prepare citations only if the answer has sufficient evidence citations = [] if not any(keyword in answer.lower() for keyword in insufficient_keywords): for i, result in enumerate(search_results[:3]): citations.append({ 'page': result['page'], 'text': result['text'][:150] + "..." if len(result['text']) > 150 else result['text'], 'score': round(result['score'], 3) }) return answer, citations except Exception as e: return f"Sorry, I encountered an error: {str(e)}", [] def render_sidebar(): """Render the document library sidebar""" with st.sidebar: # Header st.markdown("""
No documents yet
Upload your first PDF to get started
Upload medical documents or select from your library to start AI-powered medical Q&A
β¨ Powered by OpenAI GPT-5-mini & Qdrant Cloud β’ Optimized for Medical Education
Powered by Qdrant Vector Database & OpenAI GPT-4o-mini