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
st.set_page_config(page_title="RAG PDF Summarizer")
st.title("RAG-powered PDF Summarizer")
API_KEY = "Your API Key"
BASE_URL = "https://yourbaseurl.com"
if not API_KEY:
st.error("GENAI_API_KEY is not configured.")
st.stop()
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,
)
uploaded_file = st.file_up(
"Upload a PDF",
type=["pdf"],
)
if uploaded_file is not None:
temp_path = None
try:
with tempfile.NamedTemporaryFile(
delete=False,
suffix=".pdf",
) as temp_file:
temp_file.write(uploaded_file.getvalue())
temp_path = temp_file.name
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()
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.")
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",
)
retriever = vectordb.as_retriever(
search_type="similarity",
search_kwargs={"k": min(5, len(chunks))},
)
rag_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=retriever,
return_source_documents=True,
)
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})
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)
source & further reading
dev.to — original article
I audited 8,900 MCP servers and agent skills for clean links and licence data
Cloudflare's KV Cache Exposure Leaked Other Customers' Prompts for Two Weeks
Ghost in the Machine 👻: Creating Ghost Stories with Local AI