import asyncio import tempfile import os import fitz # PyMuPDF import io import streamlit as st from PIL import Image from langchain_community.document_loaders import PyPDFLoader from langchain_text_splitters import RecursiveCharacterTextSplitter from langchain_community.embeddings import HuggingFaceEmbeddings from langchain_community.vectorstores import FAISS from langchain.chains import RetrievalQA from langchain_community.llms import HuggingFacePipeline from transformers import AutoTokenizer, pipeline, AutoModelForSeq2SeqLM # Fix for event loop issues if os.name == 'nt': asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) MODEL_NAME = "google/flan-t5-base" EMBEDDING_MODEL = "sentence-transformers/all-MiniLM-L6-v2" CHUNK_SIZE = 500 CHUNK_OVERLAP = 50 def initialize_general_model(): """Initialize the model for general knowledge questions""" tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME) model = AutoModelForSeq2SeqLM.from_pretrained(MODEL_NAME) return pipeline( "text2text-generation", model=model, tokenizer=tokenizer, max_length=256, temperature=0, repetition_penalty=1.2 ) def create_vector_store(pdf_path): """Process PDF and create FAISS vector store""" loader = PyPDFLoader(pdf_path) pages = loader.load_and_split() text_splitter = RecursiveCharacterTextSplitter( chunk_size=CHUNK_SIZE, chunk_overlap=CHUNK_OVERLAP ) texts = text_splitter.split_documents(pages) embeddings = HuggingFaceEmbeddings(model_name=EMBEDDING_MODEL) return FAISS.from_documents(texts, embeddings) def create_qa_chain(vectorstore): """Create the Retrieval QA chain for PDF content""" pipe = initialize_general_model() llm = HuggingFacePipeline(pipeline=pipe) return RetrievalQA.from_chain_type( llm=llm, chain_type="stuff", retriever=vectorstore.as_retriever(), return_source_documents=True ) def render_pdf_page(pdf_bytes, page_number): """Render specific PDF page as image""" doc = fitz.open(stream=pdf_bytes, filetype="pdf") page = doc.load_page(page_number) pix = page.get_pixmap() img_bytes = pix.tobytes() return Image.open(io.BytesIO(img_bytes)) def main(): st.title("VectorAsk") st.write("Get answers with source page images!") # Initialize session states if 'pdf_bytes' not in st.session_state: st.session_state.pdf_bytes = None mode = st.radio("Select answer source:", ("PDF Content", "Text input"), horizontal=True) if mode == "PDF Content": uploaded_file = st.file_uploader("Upload PDF", type="pdf") if uploaded_file is not None: st.session_state.pdf_bytes = uploaded_file.getvalue() with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as tmp_file: tmp_file.write(st.session_state.pdf_bytes) tmp_path = tmp_file.name with st.spinner("Processing PDF..."): vectorstore = create_vector_store(tmp_path) os.remove(tmp_path) st.session_state['qa_chain'] = create_qa_chain(vectorstore) question = st.text_input("Enter your question:") if question: with st.spinner("Generating answer..."): if mode == "General Knowledge": if 'general_pipe' not in st.session_state: st.session_state.general_pipe = initialize_general_model() result = st.session_state.general_pipe( question, max_length=256, temperature=0 )[0]['generated_text'] st.subheader("Answer:") st.write(result) st.info("This answer is generated from the model's general knowledge") elif mode == "PDF Content": if 'qa_chain' not in st.session_state: st.warning("Please upload a PDF file first!") return result = st.session_state['qa_chain']({"query": question}) # Display answer st.subheader("Answer:") st.write(result["result"]) # Display source documents with images st.subheader("Source Evidence:") for doc in result["source_documents"]: page_num = doc.metadata['page'] col1, col2 = st.columns([2, 3]) with col1: try: img = render_pdf_page(st.session_state.pdf_bytes, page_num) st.image(img, caption=f"Page {page_num + 1}", use_column_width=True) except Exception as e: st.error(f"Error rendering page: {str(e)}") with col2: st.write(f"**Page {page_num + 1} Content:**") st.write(doc.page_content) st.write("---") if __name__ == "__main__": main()