Your First RAG Application for a PDF Summay A developer published a hands-on walkthrough for building a retrieval-augmented generation (RAG) PDF summarizer using Streamlit, LangChain, Chroma, and OpenAI-compatible chat and embedding models. The pipeline extracts text with pdfminer, splits it into 1,000-character chunks with 200-character overlap, indexes them in a persistent Chroma collection, and runs a RetrievalQA chain that returns source documents alongside the generated summary. The code includes guards for empty or scanned PDFs and cleans up temporary files after processing. import os import tempfile from pathlib import Path import streamlit as st import httpx from pdfminer.high level import extract text from langchain text splitters import RecursiveCharacterTextSplitter from langchain openai import ChatOpenAI, OpenAIEmbeddings from langchain community.vectorstores import Chroma from langchain classic.chains import RetrievalQA Streamlit configuration st.set page config page title="RAG PDF Summarizer" st.title "RAG-powered PDF Summarizer" API configuration API KEY = "Your API Key" BASE URL = "https://yourbaseurl.com" if not API KEY: st.error "GENAI API KEY is not configured." st.stop Keep TLS certificate verification enabled. client = httpx.Client timeout=120.0, verify=True llm = ChatOpenAI base url=BASE URL, model="azure ai/genailab-maas-DeepSeek-V3-0324", api key=API KEY, http client=client, embedding model = OpenAIEmbeddings base url=BASE URL, model="azure/genailab-maas-text-embedding-3-large", api key=API KEY, http client=client, PDF upload uploaded file = st.file uploader "Upload a PDF", type= "pdf" , if uploaded file is not None: temp path = None try: Step 1: Save the uploaded PDF temporarily with tempfile.NamedTemporaryFile delete=False, suffix=".pdf", as temp file: temp file.write uploaded file.getvalue temp path = temp file.name Step 2: Extract text with st.spinner "Extracting PDF text..." : raw text = extract text temp path if not raw text or not raw text.strip : st.error "No readable text found. " "If this is a scanned PDF, OCR is required." st.stop Step 3: Split text into chunks text splitter = RecursiveCharacterTextSplitter chunk size=1000, chunk overlap=200, chunks = text splitter.split text raw text if not chunks: st.error "No text chunks were generated." st.stop st.write f"Created {len chunks } text chunks." Step 4: Create the Chroma vector database chroma dir = Path.home / "rag chroma data" chroma dir.mkdir parents=True, exist ok=True with st.spinner "Indexing PDF..." : vectordb = Chroma.from texts texts=chunks, embedding=embedding model, persist directory=str chroma dir , collection name="pdf collection", Step 5: Create retriever retriever = vectordb.as retriever search type="similarity", search kwargs={"k": min 5, len chunks }, Step 6: Create the RAG chain rag chain = RetrievalQA.from chain type llm=llm, chain type="stuff", retriever=retriever, return source documents=True, Step 7: Generate the summary summary prompt = "Summarize the uploaded document using the retrieved context. " "Explain the main topics, key findings, important facts, " "and conclusions. Do not invent unsupported information." with st.spinner "Generating summary..." : result = rag chain.invoke {"query": summary prompt} Step 8: Display results st.subheader "Document Summary" st.write result "result" with st.expander "View source documents" : for i, doc in enumerate result "source documents" , start=1, : st.markdown f" Source {i} " st.write doc.page content except Exception as exc: st.error f"Processing failed: {exc}" finally: if temp path and os.path.exists temp path : os.remove temp path