#!/usr/bin/env python3 """ PDF Chat Web Application - Hugging Face Spaces Version ====================================================== A Streamlit web app for chatting with PDF documents using OpenAI. Deployed on Hugging Face Spaces for public access. """ import streamlit as st import os import tempfile import PyPDF2 from io import BytesIO import requests import json # Page configuration st.set_page_config( page_title="PDF Chat Assistant", page_icon="📄", layout="wide", initial_sidebar_state="expanded" ) # Custom CSS for modern dark theme st.markdown(""" """, unsafe_allow_html=True) class PDFChatBot: def __init__(self): self.pdf_text = "" self.conversation_history = [] self.pdf_pages = 0 self.pdf_chars = 0 def extract_pdf_text(self, pdf_file): """Extract text from PDF using PyPDF2""" try: pdf_reader = PyPDF2.PdfReader(pdf_file) text = "" page_count = len(pdf_reader.pages) for page in pdf_reader.pages: text += page.extract_text() + "\n" if not text.strip(): return False, "Could not extract text from PDF. The PDF might contain only images or be password protected." self.pdf_text = text self.pdf_pages = page_count self.pdf_chars = len(text) return True, f"Successfully extracted text from {page_count} pages ({len(text):,} characters)!" except Exception as e: return False, f"Error reading PDF: {str(e)}" def ask_openai(self, question, api_key): """Ask OpenAI directly using the API""" try: # Limit context to prevent token limits context_limit = 3000 context = f"Based on the following document content, please answer the question accurately and concisely.\n\nDocument:\n{self.pdf_text[:context_limit]}\n\nQuestion: {question}" headers = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" } data = { "model": "gpt-3.5-turbo", "messages": [ {"role": "system", "content": "You are a helpful AI assistant that answers questions about documents. Be accurate, concise, and helpful. If you cannot find the answer in the document, say so clearly."}, {"role": "user", "content": context} ], "max_tokens": 1000, "temperature": 0.1 } response = requests.post( "https://api.openai.com/v1/chat/completions", headers=headers, json=data, timeout=30 ) if response.status_code == 200: result = response.json() answer = result['choices'][0]['message']['content'] # Store in conversation history self.conversation_history.append({"question": question, "answer": answer}) return answer elif response.status_code == 401: return "❌ Invalid API key. Please check your OpenAI API key and try again." elif response.status_code == 429: return "âŗ Rate limit exceeded. Please wait a moment and try again." else: return f"❌ API Error: {response.status_code} - Please check your API key and try again." except requests.exceptions.Timeout: return "âŗ Request timed out. Please try again." except requests.exceptions.ConnectionError: return "🌐 Connection error. Please check your internet connection." except Exception as e: return f"❌ Error: {str(e)}" def main(): # Initialize session state if 'bot' not in st.session_state: st.session_state.bot = PDFChatBot() if 'messages' not in st.session_state: st.session_state.messages = [] if 'pdf_processed' not in st.session_state: st.session_state.pdf_processed = False if 'uploaded_file_name' not in st.session_state: st.session_state.uploaded_file_name = "" # Header st.markdown("""

📄 PDF Chat Assistant

Upload any PDF and start an intelligent conversation with your document!

Powered by OpenAI GPT â€ĸ Built with â¤ī¸ for Hugging Face Spaces

""", unsafe_allow_html=True) # Sidebar with st.sidebar: st.markdown("### 🔑 Configuration") api_key = st.text_input( "OpenAI API Key", type="password", help="Enter your OpenAI API key to start chatting with PDFs", placeholder="sk-..." ) if api_key: st.success("✅ API Key Provided") else: st.error("❌ API Key Required") st.markdown("---") st.markdown("### 📚 How to Use") st.markdown(""" 1. **🔑 Enter your OpenAI API key** above 2. **📤 Upload a PDF file** using the uploader 3. **âŗ Wait** for text extraction (few seconds) 4. **đŸ’Ŧ Ask questions** about your document 5. **🧠 Get AI-powered answers** instantly! """) st.markdown("---") st.markdown("### đŸŽ¯ Features") st.markdown(""" â€ĸ 📄 **PDF Text Extraction** â€ĸ 🤖 **AI-Powered Q&A** â€ĸ 💾 **Conversation Memory** â€ĸ 🎨 **Beautiful Interface** â€ĸ 🚀 **Fast & Responsive** â€ĸ 🔒 **Privacy Focused** """) st.markdown("---") if st.button("đŸ—‘ī¸ Clear Chat History", use_container_width=True): st.session_state.messages = [] st.session_state.bot = PDFChatBot() st.session_state.pdf_processed = False st.session_state.uploaded_file_name = "" st.success("✅ Chat history cleared!") st.rerun() # API Key Info with st.expander("â„šī¸ Get OpenAI API Key"): st.markdown(""" **How to get your API key:** 1. Go to [OpenAI Platform](https://platform.openai.com) 2. Sign up or log in to your account 3. Navigate to **API Keys** section 4. Click **"Create new secret key"** 5. Copy the key (starts with `sk-`) 6. Paste it in the field above **Note:** Your API key is only used for this session and is not stored anywhere. """) # Status st.markdown("---") st.markdown("### 📊 Status") if st.session_state.pdf_processed: st.success("✅ PDF Ready") st.info(f"📄 {st.session_state.uploaded_file_name}") # Display PDF stats bot = st.session_state.bot st.markdown(f""" **📈 Document Stats:** - Pages: {bot.pdf_pages} - Characters: {bot.pdf_chars:,} - Conversations: {len(bot.conversation_history)} """) else: st.warning("âŗ No PDF loaded") if api_key: st.success("✅ API Connected") else: st.error("❌ API Key Missing") # Main content col1, col2 = st.columns([1, 2]) with col1: st.markdown("### 📤 Upload Your PDF") uploaded_file = st.file_uploader( "Choose a PDF file", type="pdf", help="Upload any PDF document (max 200MB)", label_visibility="collapsed" ) if not uploaded_file: st.markdown("""

📁 Drag & Drop Your PDF Here

Or click "Browse files" above to select a PDF


📋 Supported: PDF files up to 200MB
🔒 Your files are processed securely and not stored

""", unsafe_allow_html=True) if uploaded_file and api_key: if not st.session_state.pdf_processed or st.session_state.uploaded_file_name != uploaded_file.name: with st.spinner("📖 Extracting text from your PDF..."): success, message = st.session_state.bot.extract_pdf_text(uploaded_file) if success: st.markdown(f"""

✅ PDF Processed Successfully!

{message}

""", unsafe_allow_html=True) st.session_state.pdf_processed = True st.session_state.uploaded_file_name = uploaded_file.name # Show file details file_size = uploaded_file.size / 1024 # KB bot = st.session_state.bot st.markdown(f"""
📄 File: {uploaded_file.name}
📊 Size: {file_size:.1f} KB
📃 Pages: {bot.pdf_pages}
📝 Characters: {bot.pdf_chars:,}
đŸŽ¯ Status: Ready for questions!
""", unsafe_allow_html=True) else: st.markdown(f"""

âš ī¸ Processing Failed

{message}

""", unsafe_allow_html=True) elif uploaded_file and not api_key: st.markdown("""

âš ī¸ API Key Required

Please enter your OpenAI API key in the sidebar to process the PDF.

""", unsafe_allow_html=True) # Example questions if st.session_state.pdf_processed: st.markdown("### 💡 Try These Questions") example_questions = [ "📋 What is this document about?", "📝 Summarize the main points", "🔍 What are the key details?", "📊 Give me important information", "❓ What questions can I ask?" ] for question in example_questions: if st.button(question, key=f"example_{question}", use_container_width=True): # Trigger the question question_text = question.split(" ", 1)[1] # Remove emoji st.session_state.pending_question = question_text st.rerun() with col2: st.markdown("### đŸ’Ŧ Chat with Your PDF") # Chat container chat_container = st.container() with chat_container: if st.session_state.messages: for message in st.session_state.messages: if message["role"] == "user": st.markdown(f"""
🧑 You: {message["content"]}
""", unsafe_allow_html=True) else: st.markdown(f"""
🤖 AI: {message["content"]}
""", unsafe_allow_html=True) else: if st.session_state.pdf_processed: st.markdown("""
🤖 AI: Hello! I've analyzed your PDF document. What would you like to know about it? Feel free to ask any questions!
""", unsafe_allow_html=True) else: st.markdown("""

👋 Welcome to PDF Chat Assistant!

Transform any PDF into an interactive conversation


📄 Smart

AI understands your documents

⚡ Fast

Instant answers to your questions

🔒 Secure

Your data stays private


Get started: Add your API key and upload a PDF!

""", unsafe_allow_html=True) # Input area st.markdown("---") # Check for pending question from example buttons if hasattr(st.session_state, 'pending_question'): user_question = st.session_state.pending_question del st.session_state.pending_question else: user_question = st.text_input( "Ask a question about your PDF:", placeholder="e.g., What are the main topics discussed in this document?", disabled=not st.session_state.pdf_processed, key="user_input" ) col_btn1, col_btn2, col_btn3 = st.columns([2, 1, 1]) with col_btn1: send_button = st.button("📤 Send Message", disabled=not st.session_state.pdf_processed, use_container_width=True) with col_btn2: if st.button("🔄 Refresh", disabled=not st.session_state.pdf_processed, use_container_width=True): st.rerun() # Process question if (send_button or hasattr(st.session_state, 'pending_question')) and user_question and st.session_state.pdf_processed and api_key: # Add user message st.session_state.messages.append({"role": "user", "content": user_question}) # Get AI response with st.spinner("🤖 AI is analyzing your question..."): ai_response = st.session_state.bot.ask_openai(user_question, api_key) # Add AI response st.session_state.messages.append({"role": "assistant", "content": ai_response}) st.rerun() elif send_button and not st.session_state.pdf_processed: st.warning("âš ī¸ Please upload and process a PDF first!") elif send_button and not api_key: st.warning("âš ī¸ Please enter your OpenAI API key in the sidebar!") # Footer st.markdown("---") st.markdown("""

🚀 PDF Chat Assistant

Made with â¤ī¸ using Streamlit â€ĸ Powered by OpenAI GPT-3.5 â€ĸ Hosted on 🤗 Hugging Face Spaces

📄 Upload PDFs â€ĸ đŸ’Ŧ Ask Questions â€ĸ 🧠 Get AI Answers â€ĸ 🔒 Privacy First


⭐ Like this app? Give it a star on Hugging Face Spaces!

""", unsafe_allow_html=True) if __name__ == "__main__": main()