Ronak0 commited on
Commit
a92f235
·
verified ·
1 Parent(s): efd7fdd

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +117 -0
app.py ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import tempfile
3
+ import fitz # PyMuPDF
4
+ import streamlit as st
5
+ import torch
6
+ from transformers import AutoConfig, AutoTokenizer, T5ForConditionalGeneration
7
+
8
+ # Page Configuration
9
+ st.set_page_config(
10
+ page_title="RTI & Civic PDF Simplifier",
11
+ page_icon="📄",
12
+ layout="centered",
13
+ )
14
+
15
+ st.title("📄 Civic & RTI PDF Simplifier")
16
+ st.markdown(
17
+ "Upload bureaucratic, legal, or RTI PDF documents to generate concise,"
18
+ " plain-language bullet points."
19
+ )
20
+
21
+ MODEL_REPO = "CaseLoop/pdf"
22
+ FALLBACK_BASE = "google/flan-t5-base"
23
+
24
+
25
+ @st.cache_resource(show_spinner="Loading model weights...")
26
+ def load_model_and_tokenizer():
27
+ # Tokenizer fallback
28
+ try:
29
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_REPO, legacy=False)
30
+ except Exception:
31
+ tokenizer = AutoTokenizer.from_pretrained(FALLBACK_BASE, legacy=False)
32
+
33
+ # Model loader with architecture fallback
34
+ try:
35
+ model = T5ForConditionalGeneration.from_pretrained(MODEL_REPO)
36
+ except Exception:
37
+ try:
38
+ config = AutoConfig.from_pretrained(FALLBACK_BASE)
39
+ model = T5ForConditionalGeneration.from_pretrained(
40
+ MODEL_REPO, config=config
41
+ )
42
+ except Exception:
43
+ model = T5ForConditionalGeneration.from_pretrained(FALLBACK_BASE)
44
+
45
+ model.eval()
46
+ return tokenizer, model
47
+
48
+
49
+ tokenizer, model = load_model_and_tokenizer()
50
+
51
+
52
+ def extract_chunks(
53
+ pdf_path: str, chunk_size: int = 180, max_chunks: int = 6
54
+ ) -> list[str]:
55
+ """Extracts lines from a PDF and chunks words for FLAN-T5 input limits."""
56
+ doc = fitz.open(pdf_path)
57
+ lines = []
58
+ for page in doc:
59
+ text = page.get_text("text")
60
+ lines.extend([
61
+ line.strip()
62
+ for line in text.split("\n")
63
+ if len(line.strip().split()) >= 5
64
+ ])
65
+
66
+ words = " ".join(lines).split()
67
+ if not words:
68
+ return []
69
+
70
+ chunks = [
71
+ " ".join(words[i : i + chunk_size])
72
+ for i in range(0, len(words), chunk_size)
73
+ ]
74
+ return chunks[:max_chunks]
75
+
76
+
77
+ # UI File Uploader
78
+ uploaded_file = st.file_uploader(
79
+ "Choose a PDF file", type=["pdf"], help="Upload an RTI reply or civic notice"
80
+ )
81
+
82
+ if uploaded_file is not None:
83
+ if st.button("Simplify Document", type="primary"):
84
+ with st.spinner("Parsing document and running simplification..."):
85
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as tmp:
86
+ tmp.write(uploaded_file.read())
87
+ tmp_path = tmp.name
88
+
89
+ chunks = extract_chunks(tmp_path)
90
+ if os.path.exists(tmp_path):
91
+ os.remove(tmp_path)
92
+
93
+ if not chunks:
94
+ st.warning(
95
+ "No extractable text found in this PDF. It might be a scanned image."
96
+ )
97
+ else:
98
+ st.subheader("Key Takeaways in Plain Language:")
99
+ for idx, chunk in enumerate(chunks, 1):
100
+ prompt = f"simplify bureaucratic text into simple language: {chunk}"
101
+ inputs = tokenizer(
102
+ prompt, return_tensors="pt", max_length=512, truncation=True
103
+ )
104
+
105
+ with torch.no_grad():
106
+ output_tokens = model.generate(
107
+ **inputs,
108
+ max_length=120,
109
+ min_length=20,
110
+ num_beams=4,
111
+ no_repeat_ngram_size=3,
112
+ repetition_penalty=2.5,
113
+ early_stopping=True,
114
+ )
115
+
116
+ summary = tokenizer.decode(output_tokens[0], skip_special_tokens=True)
117
+ st.markdown(f"**{idx}.** {summary}")