pdf / app.py
Ronak0's picture
Create app.py
a92f235 verified
Raw History Blame
3.36 kB
import os
import tempfile
import fitz # PyMuPDF
import streamlit as st
import torch
from transformers import AutoConfig, AutoTokenizer, T5ForConditionalGeneration
# Page Configuration
st.set_page_config(
page_title="RTI & Civic PDF Simplifier",
page_icon="📄",
layout="centered",
)
st.title("📄 Civic & RTI PDF Simplifier")
st.markdown(
"Upload bureaucratic, legal, or RTI PDF documents to generate concise,"
" plain-language bullet points."
)
MODEL_REPO = "CaseLoop/pdf"
FALLBACK_BASE = "google/flan-t5-base"
@st.cache_resource(show_spinner="Loading model weights...")
def load_model_and_tokenizer():
# Tokenizer fallback
try:
tokenizer = AutoTokenizer.from_pretrained(MODEL_REPO, legacy=False)
except Exception:
tokenizer = AutoTokenizer.from_pretrained(FALLBACK_BASE, legacy=False)
# Model loader with architecture fallback
try:
model = T5ForConditionalGeneration.from_pretrained(MODEL_REPO)
except Exception:
try:
config = AutoConfig.from_pretrained(FALLBACK_BASE)
model = T5ForConditionalGeneration.from_pretrained(
MODEL_REPO, config=config
)
except Exception:
model = T5ForConditionalGeneration.from_pretrained(FALLBACK_BASE)
model.eval()
return tokenizer, model
tokenizer, model = load_model_and_tokenizer()
def extract_chunks(
pdf_path: str, chunk_size: int = 180, max_chunks: int = 6
) -> list[str]:
"""Extracts lines from a PDF and chunks words for FLAN-T5 input limits."""
doc = fitz.open(pdf_path)
lines = []
for page in doc:
text = page.get_text("text")
lines.extend([
line.strip()
for line in text.split("\n")
if len(line.strip().split()) >= 5
])
words = " ".join(lines).split()
if not words:
return []
chunks = [
" ".join(words[i : i + chunk_size])
for i in range(0, len(words), chunk_size)
]
return chunks[:max_chunks]
# UI File Uploader
uploaded_file = st.file_uploader(
"Choose a PDF file", type=["pdf"], help="Upload an RTI reply or civic notice"
)
if uploaded_file is not None:
if st.button("Simplify Document", type="primary"):
with st.spinner("Parsing document and running simplification..."):
with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as tmp:
tmp.write(uploaded_file.read())
tmp_path = tmp.name
chunks = extract_chunks(tmp_path)
if os.path.exists(tmp_path):
os.remove(tmp_path)
if not chunks:
st.warning(
"No extractable text found in this PDF. It might be a scanned image."
)
else:
st.subheader("Key Takeaways in Plain Language:")
for idx, chunk in enumerate(chunks, 1):
prompt = f"simplify bureaucratic text into simple language: {chunk}"
inputs = tokenizer(
prompt, return_tensors="pt", max_length=512, truncation=True
)
with torch.no_grad():
output_tokens = model.generate(
**inputs,
max_length=120,
min_length=20,
num_beams=4,
no_repeat_ngram_size=3,
repetition_penalty=2.5,
early_stopping=True,
)
summary = tokenizer.decode(output_tokens[0], skip_special_tokens=True)
st.markdown(f"**{idx}.** {summary}")