Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import os | |
| import logging | |
| from io import BytesIO | |
| from PyPDF2 import PdfReader | |
| from langchain.text_splitter import CharacterTextSplitter | |
| from langchain_community.embeddings import HuggingFaceEmbeddings | |
| from langchain_community.vectorstores import FAISS | |
| from langchain.prompts import PromptTemplate | |
| from langchain.chains.question_answering import load_qa_chain | |
| from langchain_community.llms import HuggingFaceHub | |
| from transformers import pipeline # For fallback if Hub fails | |
| # Set up logging | |
| logging.basicConfig(level=logging.INFO) | |
| logger = logging.getLogger(__name__) | |
| # Check API token | |
| if "HUGGINGFACEHUB_API_TOKEN" not in os.environ: | |
| st.error("HUGGINGFACEHUB_API_TOKEN not set in secrets. Add it in Space settings.") | |
| st.stop() | |
| try: | |
| # Function to process PDF | |
| def process_pdf(uploaded_file): | |
| try: | |
| logger.info("Starting PDF processing") | |
| pdf_reader = PdfReader(BytesIO(uploaded_file.getvalue())) | |
| text = "" | |
| for page in pdf_reader.pages: | |
| extracted = page.extract_text() | |
| if extracted: | |
| text += extracted + "\n" | |
| if not text: | |
| raise ValueError("No text extracted from PDF.") | |
| # Chunk text (increased overlap for better context) | |
| text_splitter = CharacterTextSplitter(separator="\n", chunk_size=800, chunk_overlap=200, length_function=len) | |
| chunks = text_splitter.split_text(text) | |
| # Embeddings (light model) | |
| embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2", model_kwargs={'device': 'cpu'}) | |
| # Vector store | |
| vector_store = FAISS.from_texts(chunks, embedding=embeddings) | |
| logger.info("PDF processed successfully") | |
| return vector_store | |
| except Exception as e: | |
| logger.error(f"PDF processing error: {str(e)}") | |
| st.error(f"Error processing PDF: {str(e)}") | |
| return None | |
| # Function to answer questions | |
| def answer_question(vector_store, query): | |
| try: | |
| logger.info(f"Answering query: {query}") | |
| # Lighter LLM via pipeline for faster CPU inference | |
| qa_pipeline = pipeline("text2text-generation", model="google/flan-t5-base") | |
| # Retrieve top chunks | |
| docs = vector_store.similarity_search(query, k=3) | |
| context = "\n".join([doc.page_content for doc in docs]) | |
| # Prompt | |
| prompt = f"Use this context to answer concisely: {context}\nQuestion: {query}\nAnswer:" | |
| response = qa_pipeline(prompt, max_length=256, num_return_sequences=1)[0]['generated_text'] | |
| logger.info("Answer generated") | |
| return response.strip() | |
| except Exception as e: | |
| logger.error(f"Answer generation error: {str(e)}") | |
| st.error(f"Error answering: {str(e)}") | |
| return "Unable to generate answer." | |
| # Streamlit UI with chat history | |
| st.title("Smart PDF Q&A") | |
| st.write("Upload a PDF and ask questions! Chat history is preserved.") | |
| # Initialize session state | |
| if "messages" not in st.session_state: | |
| st.session_state.messages = [] | |
| if "vector_store" not in st.session_state: | |
| st.session_state.vector_store = None | |
| # PDF upload and process | |
| uploaded_file = st.file_uploader("Upload PDF", type="pdf") | |
| if uploaded_file: | |
| if st.button("Process PDF"): | |
| with st.spinner("Processing..."): | |
| vector_store = process_pdf(uploaded_file) | |
| if vector_store: | |
| st.session_state.vector_store = vector_store | |
| st.success("PDF ready! Ask away.") | |
| st.session_state.messages = [] # Reset chat on new PDF | |
| # Display chat history | |
| for message in st.session_state.messages: | |
| with st.chat_message(message["role"]): | |
| st.markdown(message["content"]) | |
| # Question input | |
| if st.session_state.vector_store: | |
| if prompt := st.chat_input("Ask a question:"): | |
| # Add user message | |
| st.session_state.messages.append({"role": "user", "content": prompt}) | |
| with st.chat_message("user"): | |
| st.markdown(prompt) | |
| # Generate answer | |
| with st.chat_message("assistant"): | |
| with st.spinner("Thinking..."): | |
| answer = answer_question(st.session_state.vector_store, prompt) | |
| st.markdown(answer) | |
| st.session_state.messages.append({"role": "assistant", "content": answer}) | |
| except Exception as e: | |
| logger.error(f"App initialization failed: {str(e)}") | |
| st.error(f"Initialization error: {str(e)}. Check logs or try factory reset.") |