Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -11,7 +11,7 @@ GEMINI_API_KEY = st.secrets["GEMINI_API_KEY"]
|
|
11 |
GEMINI_MODEL = "gemini-2-flash"
|
12 |
DOCUMENT_TYPES = ["Land Records", "Caste Certificates", "Property Registrations"]
|
13 |
|
14 |
-
# Initialize session state
|
15 |
def initialize_session_state():
|
16 |
if "chat_history" not in st.session_state:
|
17 |
st.session_state["chat_history"] = []
|
@@ -19,12 +19,10 @@ def initialize_session_state():
|
|
19 |
st.session_state["processed_doc"] = None
|
20 |
if "doc_preview" not in st.session_state:
|
21 |
st.session_state["doc_preview"] = None
|
22 |
-
if "uploaded_file" not in st.session_state:
|
23 |
-
st.session_state["uploaded_file"] = None
|
24 |
|
25 |
-
# Reset session state
|
26 |
def reset_session_state():
|
27 |
-
for key in ["chat_history", "processed_doc", "doc_preview"
|
28 |
st.session_state.pop(key, None)
|
29 |
|
30 |
# Encode uploaded file to base64
|
@@ -37,11 +35,11 @@ def encode_file(uploaded_file):
|
|
37 |
page = pdf[0]
|
38 |
pix = page.get_pixmap()
|
39 |
img = Image.frombytes("RGB", [pix.width, pix.height], pix.samples)
|
40 |
-
elif uploaded_file.type.startswith(
|
41 |
img = Image.open(BytesIO(file_bytes))
|
42 |
elif uploaded_file.type == "text/plain":
|
43 |
-
text = file_bytes.decode(
|
44 |
-
img = Image.new(
|
45 |
d = ImageDraw.Draw(img)
|
46 |
d.text((10, 10), text, fill=(255, 255, 0))
|
47 |
else:
|
@@ -49,8 +47,9 @@ def encode_file(uploaded_file):
|
|
49 |
return None
|
50 |
|
51 |
img_byte_arr = BytesIO()
|
52 |
-
img.save(img_byte_arr, format=
|
53 |
-
return base64.b64encode(img_byte_arr.getvalue()).decode(
|
|
|
54 |
except Exception as e:
|
55 |
st.error(f"File processing error: {str(e)}")
|
56 |
return None
|
@@ -58,9 +57,15 @@ def encode_file(uploaded_file):
|
|
58 |
# Query Gemini API
|
59 |
def query_gemini(prompt, image_b64=None):
|
60 |
url = f"https://generativelanguage.googleapis.com/v1/models/{GEMINI_MODEL}:generateContent?key={GEMINI_API_KEY}"
|
|
|
61 |
parts = [{"text": prompt}]
|
62 |
if image_b64:
|
63 |
-
parts.append({
|
|
|
|
|
|
|
|
|
|
|
64 |
|
65 |
try:
|
66 |
response = requests.post(
|
@@ -74,16 +79,17 @@ def query_gemini(prompt, image_b64=None):
|
|
74 |
return None
|
75 |
|
76 |
data = response.json()
|
77 |
-
if
|
78 |
st.error(f"API Error: {data['error'].get('message', 'Unknown error')}")
|
79 |
return None
|
80 |
-
|
81 |
-
if not data.get(
|
82 |
st.error("No response candidates found in API response")
|
83 |
return None
|
84 |
|
85 |
-
candidate = data[
|
86 |
-
return candidate.get(
|
|
|
87 |
except requests.exceptions.RequestException as e:
|
88 |
st.error(f"API Request failed: {str(e)}")
|
89 |
return None
|
@@ -93,26 +99,46 @@ def query_gemini(prompt, image_b64=None):
|
|
93 |
|
94 |
# Process the uploaded document
|
95 |
def process_document():
|
96 |
-
|
|
|
97 |
st.error("Please upload a document first.")
|
98 |
return
|
|
|
99 |
try:
|
100 |
with st.spinner("Analyzing document..."):
|
101 |
-
|
|
|
102 |
if not image_b64:
|
103 |
return
|
104 |
|
105 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
106 |
doc_type = query_gemini(classify_prompt, image_b64)
|
107 |
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
|
|
|
|
|
|
114 |
details = query_gemini(extract_prompt, image_b64)
|
115 |
-
|
|
|
116 |
verify_prompt = "Analyze this document for signs of tampering. Provide verification status."
|
117 |
verification = query_gemini(verify_prompt, image_b64)
|
118 |
|
@@ -121,8 +147,10 @@ def process_document():
|
|
121 |
"details": details or "No details extracted",
|
122 |
"verification": verification or "Verification failed",
|
123 |
}
|
|
|
124 |
st.success("Document processing complete!")
|
125 |
time.sleep(1)
|
|
|
126 |
except Exception as e:
|
127 |
st.error(f"Document processing failed: {str(e)}")
|
128 |
st.session_state.processed_doc = None
|
@@ -132,20 +160,45 @@ def main():
|
|
132 |
st.set_page_config(page_title="DocVerify AI", layout="wide")
|
133 |
initialize_session_state()
|
134 |
|
135 |
-
|
136 |
-
st.sidebar
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
143 |
|
144 |
-
if st.session_state.processed_doc:
|
145 |
-
st.
|
146 |
-
|
147 |
-
|
148 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
149 |
|
150 |
if __name__ == "__main__":
|
151 |
main()
|
|
|
11 |
GEMINI_MODEL = "gemini-2-flash"
|
12 |
DOCUMENT_TYPES = ["Land Records", "Caste Certificates", "Property Registrations"]
|
13 |
|
14 |
+
# Initialize session state (excluding widget-controlled keys)
|
15 |
def initialize_session_state():
|
16 |
if "chat_history" not in st.session_state:
|
17 |
st.session_state["chat_history"] = []
|
|
|
19 |
st.session_state["processed_doc"] = None
|
20 |
if "doc_preview" not in st.session_state:
|
21 |
st.session_state["doc_preview"] = None
|
|
|
|
|
22 |
|
23 |
+
# Reset session state (excluding widget-controlled keys)
|
24 |
def reset_session_state():
|
25 |
+
for key in ["chat_history", "processed_doc", "doc_preview"]:
|
26 |
st.session_state.pop(key, None)
|
27 |
|
28 |
# Encode uploaded file to base64
|
|
|
35 |
page = pdf[0]
|
36 |
pix = page.get_pixmap()
|
37 |
img = Image.frombytes("RGB", [pix.width, pix.height], pix.samples)
|
38 |
+
elif uploaded_file.type.startswith("image/"):
|
39 |
img = Image.open(BytesIO(file_bytes))
|
40 |
elif uploaded_file.type == "text/plain":
|
41 |
+
text = file_bytes.decode("utf-8")
|
42 |
+
img = Image.new("RGB", (800, 600), color=(73, 109, 137))
|
43 |
d = ImageDraw.Draw(img)
|
44 |
d.text((10, 10), text, fill=(255, 255, 0))
|
45 |
else:
|
|
|
47 |
return None
|
48 |
|
49 |
img_byte_arr = BytesIO()
|
50 |
+
img.save(img_byte_arr, format="JPEG")
|
51 |
+
return base64.b64encode(img_byte_arr.getvalue()).decode("utf-8")
|
52 |
+
|
53 |
except Exception as e:
|
54 |
st.error(f"File processing error: {str(e)}")
|
55 |
return None
|
|
|
57 |
# Query Gemini API
|
58 |
def query_gemini(prompt, image_b64=None):
|
59 |
url = f"https://generativelanguage.googleapis.com/v1/models/{GEMINI_MODEL}:generateContent?key={GEMINI_API_KEY}"
|
60 |
+
|
61 |
parts = [{"text": prompt}]
|
62 |
if image_b64:
|
63 |
+
parts.append({
|
64 |
+
"inline_data": {
|
65 |
+
"mime_type": "image/jpeg",
|
66 |
+
"data": image_b64
|
67 |
+
}
|
68 |
+
})
|
69 |
|
70 |
try:
|
71 |
response = requests.post(
|
|
|
79 |
return None
|
80 |
|
81 |
data = response.json()
|
82 |
+
if "error" in data:
|
83 |
st.error(f"API Error: {data['error'].get('message', 'Unknown error')}")
|
84 |
return None
|
85 |
+
|
86 |
+
if not data.get("candidates"):
|
87 |
st.error("No response candidates found in API response")
|
88 |
return None
|
89 |
|
90 |
+
candidate = data["candidates"][0]
|
91 |
+
return candidate.get("content", {}).get("parts", [{}])[0].get("text", "No response text found")
|
92 |
+
|
93 |
except requests.exceptions.RequestException as e:
|
94 |
st.error(f"API Request failed: {str(e)}")
|
95 |
return None
|
|
|
99 |
|
100 |
# Process the uploaded document
|
101 |
def process_document():
|
102 |
+
uploaded_file = st.session_state.get("uploaded_file")
|
103 |
+
if not uploaded_file:
|
104 |
st.error("Please upload a document first.")
|
105 |
return
|
106 |
+
|
107 |
try:
|
108 |
with st.spinner("Analyzing document..."):
|
109 |
+
# Encode file to base64
|
110 |
+
image_b64 = encode_file(uploaded_file)
|
111 |
if not image_b64:
|
112 |
return
|
113 |
|
114 |
+
# Store preview image
|
115 |
+
if uploaded_file.type == "application/pdf":
|
116 |
+
pdf = fitz.open(stream=BytesIO(uploaded_file.getvalue()))
|
117 |
+
page = pdf[0]
|
118 |
+
pix = page.get_pixmap()
|
119 |
+
st.session_state.doc_preview = Image.frombytes("RGB", [pix.width, pix.height], pix.samples)
|
120 |
+
else:
|
121 |
+
st.session_state.doc_preview = Image.open(uploaded_file)
|
122 |
+
|
123 |
+
# Classify document
|
124 |
+
classify_prompt = (
|
125 |
+
f"Classify this document into one of these categories: {', '.join(DOCUMENT_TYPES)}. "
|
126 |
+
"Respond only with the category name."
|
127 |
+
)
|
128 |
doc_type = query_gemini(classify_prompt, image_b64)
|
129 |
|
130 |
+
# Extract details
|
131 |
+
extract_prompt = (
|
132 |
+
"Extract key details including:\n"
|
133 |
+
"- Names\n"
|
134 |
+
"- Dates\n"
|
135 |
+
"- Identification numbers\n"
|
136 |
+
"- Locations\n"
|
137 |
+
"Format as a bullet-point list."
|
138 |
+
)
|
139 |
details = query_gemini(extract_prompt, image_b64)
|
140 |
+
|
141 |
+
# Verify authenticity
|
142 |
verify_prompt = "Analyze this document for signs of tampering. Provide verification status."
|
143 |
verification = query_gemini(verify_prompt, image_b64)
|
144 |
|
|
|
147 |
"details": details or "No details extracted",
|
148 |
"verification": verification or "Verification failed",
|
149 |
}
|
150 |
+
|
151 |
st.success("Document processing complete!")
|
152 |
time.sleep(1)
|
153 |
+
|
154 |
except Exception as e:
|
155 |
st.error(f"Document processing failed: {str(e)}")
|
156 |
st.session_state.processed_doc = None
|
|
|
160 |
st.set_page_config(page_title="DocVerify AI", layout="wide")
|
161 |
initialize_session_state()
|
162 |
|
163 |
+
# Sidebar Controls
|
164 |
+
with st.sidebar:
|
165 |
+
st.header("Document Controls")
|
166 |
+
# The file uploader widget manages its own state with key "uploaded_file"
|
167 |
+
st.file_uploader(
|
168 |
+
"Upload Document",
|
169 |
+
type=["pdf", "jpg", "jpeg", "png", "txt"],
|
170 |
+
key="uploaded_file",
|
171 |
+
on_change=process_document,
|
172 |
+
help="Supported formats: PDF, JPG, PNG, TXT"
|
173 |
+
)
|
174 |
+
|
175 |
+
if st.button("New Document"):
|
176 |
+
reset_session_state()
|
177 |
+
st.experimental_rerun()
|
178 |
+
|
179 |
+
if st.session_state.get("processed_doc"):
|
180 |
+
st.divider()
|
181 |
+
st.subheader("Document Summary")
|
182 |
+
st.markdown(f"**Type:** {st.session_state.processed_doc['type']}")
|
183 |
+
st.markdown(f"**Verification Status:** {st.session_state.processed_doc['verification']}")
|
184 |
+
|
185 |
+
# Main Interface
|
186 |
+
st.title("📄 Automated Document Verifier")
|
187 |
|
188 |
+
if st.session_state.get("processed_doc") and st.session_state.get("doc_preview"):
|
189 |
+
col1, col2 = st.columns([1, 2])
|
190 |
+
with col1:
|
191 |
+
st.subheader("Document Preview")
|
192 |
+
st.image(st.session_state.doc_preview, use_column_width=True)
|
193 |
+
|
194 |
+
with col2:
|
195 |
+
st.subheader("Extracted Details")
|
196 |
+
st.markdown(st.session_state.processed_doc["details"])
|
197 |
+
|
198 |
+
st.subheader("Verification Analysis")
|
199 |
+
st.markdown(st.session_state.processed_doc["verification"])
|
200 |
+
else:
|
201 |
+
st.info("Please upload a document to begin verification analysis")
|
202 |
|
203 |
if __name__ == "__main__":
|
204 |
main()
|