Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1 |
import os
|
2 |
import time
|
3 |
import hashlib
|
|
|
4 |
from dotenv import load_dotenv
|
5 |
import streamlit as st
|
6 |
from langchain_community.vectorstores import FAISS
|
@@ -74,6 +75,22 @@ st.markdown("""
|
|
74 |
max-width: 250px;
|
75 |
height: auto;
|
76 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
77 |
</style>
|
78 |
""", unsafe_allow_html=True)
|
79 |
|
@@ -90,9 +107,9 @@ if "authenticated" not in st.session_state:
|
|
90 |
|
91 |
if st.session_state.role is None:
|
92 |
st.markdown("<h2 style='text-align: center;'>Who are you?</h2>", unsafe_allow_html=True)
|
93 |
-
col1, col2, col3 = st.columns([1,
|
94 |
with col2:
|
95 |
-
col_a, col_b = st.columns(
|
96 |
with col_a:
|
97 |
if st.button("🧑 I am a Civilian"):
|
98 |
st.session_state.role = "civilian"
|
@@ -102,9 +119,13 @@ if st.session_state.role is None:
|
|
102 |
if st.button("⚖️ I am a Court Stakeholder"):
|
103 |
st.session_state.role = "stakeholder"
|
104 |
st.rerun()
|
|
|
|
|
|
|
|
|
105 |
|
106 |
-
if st.session_state.role == "stakeholder" and not st.session_state.authenticated:
|
107 |
-
st.markdown("### 🔐 Stakeholder Login")
|
108 |
username = st.text_input("Username")
|
109 |
password = st.text_input("Password", type="password")
|
110 |
if st.button("Login"):
|
@@ -122,16 +143,37 @@ if st.session_state.role and (st.session_state.role == "civilian" or st.session_
|
|
122 |
st.rerun()
|
123 |
|
124 |
tabs = ["📘 LawGPT"]
|
125 |
-
|
|
|
|
|
|
|
126 |
tabs.extend(["📝 Document Signer", "🔍 Verify Document"])
|
127 |
|
128 |
selected_tab = st.tabs(tabs)
|
129 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
130 |
if "📘 LawGPT" in tabs:
|
131 |
with selected_tab[0]:
|
132 |
st.markdown("## 💬 Your Legal AI Lawyer")
|
133 |
st.markdown("### Ask any legal question related to the Indian Penal Code (IPC)")
|
134 |
-
st.markdown("Questions might be of types like: Suppose a 16 year old is
|
135 |
|
136 |
def reset_conversation():
|
137 |
st.session_state.messages = []
|
@@ -144,14 +186,6 @@ if st.session_state.role and (st.session_state.role == "civilian" or st.session_
|
|
144 |
k=2, memory_key="chat_history", return_messages=True
|
145 |
)
|
146 |
|
147 |
-
embeddings = HuggingFaceEmbeddings(
|
148 |
-
model_name="nomic-ai/nomic-embed-text-v1",
|
149 |
-
model_kwargs={"trust_remote_code": True, "revision": "289f532e14dbbbd5a04753fa58739e9ba766f3c7"}
|
150 |
-
)
|
151 |
-
|
152 |
-
db = FAISS.load_local("ipc_vector_db", embeddings, allow_dangerous_deserialization=True)
|
153 |
-
db_retriever = db.as_retriever(search_type="similarity", search_kwargs={"k": 4})
|
154 |
-
|
155 |
prompt_template = """<s>[INST]You are a legal chatbot that answers questions about the Indian Penal Code (IPC).
|
156 |
Provide clear, concise, and accurate responses based on context and user's question.
|
157 |
Avoid extra details or assumptions. Focus only on legal information.
|
@@ -168,13 +202,6 @@ ANSWER:
|
|
168 |
input_variables=["context", "question", "chat_history"]
|
169 |
)
|
170 |
|
171 |
-
llm = Together(
|
172 |
-
model="mistralai/Mistral-7B-Instruct-v0.2",
|
173 |
-
temperature=0.5,
|
174 |
-
max_tokens=1024,
|
175 |
-
together_api_key=os.getenv("TOGETHER_API_KEY")
|
176 |
-
)
|
177 |
-
|
178 |
qa = ConversationalRetrievalChain.from_llm(
|
179 |
llm=llm,
|
180 |
memory=st.session_state.memory,
|
@@ -206,10 +233,408 @@ ANSWER:
|
|
206 |
full_response += chunk
|
207 |
time.sleep(0.02)
|
208 |
message_placeholder.markdown(full_response + " ▌")
|
|
|
209 |
st.session_state.messages.append({"role": "assistant", "content": result["answer"]})
|
210 |
|
211 |
st.button("🔄 Reset Chat", on_click=reset_conversation)
|
212 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
213 |
if st.session_state.role == "stakeholder":
|
214 |
if "📝 Document Signer" in tabs:
|
215 |
with selected_tab[1]:
|
@@ -249,130 +674,130 @@ ANSWER:
|
|
249 |
with selected_tab[2]:
|
250 |
st.markdown("## 🔍 Verify Document Authentication")
|
251 |
st.markdown("Upload any document to verify its integrity and authenticity.")
|
252 |
-
|
253 |
-
verify_file = st.file_uploader("Upload PDF for verification", type=["pdf"], key="verify")
|
254 |
-
|
255 |
-
if verify_file:
|
256 |
-
content = verify_file.read()
|
257 |
-
|
258 |
-
try:
|
259 |
-
# Basic PDF validation
|
260 |
-
pdf = PdfReader(BytesIO(content))
|
261 |
-
|
262 |
-
# Extract text to look for signature markers
|
263 |
-
all_text = ""
|
264 |
-
for page in pdf.pages:
|
265 |
-
all_text += page.extract_text() or ""
|
266 |
-
|
267 |
-
# Check for digital signature information
|
268 |
-
has_signature_text = any(sig_text in all_text.lower() for sig_text in
|
269 |
-
["signed by:", "digital signature", "electronic signature"])
|
270 |
|
271 |
-
|
272 |
-
doc_hash = hashlib.sha256(content).hexdigest()
|
273 |
|
274 |
-
|
275 |
-
|
276 |
-
if pdf.metadata:
|
277 |
-
try:
|
278 |
-
# Check for suspicious metadata modifications
|
279 |
-
creation_date = pdf.metadata.get('/CreationDate', '')
|
280 |
-
mod_date = pdf.metadata.get('/ModDate', '')
|
281 |
-
if mod_date and creation_date:
|
282 |
-
metadata_valid = mod_date >= creation_date
|
283 |
-
except:
|
284 |
-
metadata_valid = False
|
285 |
-
|
286 |
-
# Check for content consistency
|
287 |
-
content_consistent = True
|
288 |
-
|
289 |
-
col1, col2 = st.columns(2)
|
290 |
-
|
291 |
-
with col1:
|
292 |
-
st.subheader("Document Analysis")
|
293 |
-
st.info(f"📄 Pages: {len(pdf.pages)}")
|
294 |
-
st.info(f"🔒 Contains signature markers: {'Yes' if has_signature_text else 'No'}")
|
295 |
-
|
296 |
-
# Display hash for document tracking
|
297 |
-
st.code(f"Document Hash: {doc_hash[:16]}...{doc_hash[-16:]}")
|
298 |
-
|
299 |
-
# Document size and characteristics
|
300 |
-
file_size = len(content) / 1024 # KB
|
301 |
-
st.info(f"📦 File size: {file_size:.2f} KB")
|
302 |
-
|
303 |
-
with col2:
|
304 |
-
st.subheader("Verification Results")
|
305 |
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
319 |
|
320 |
-
|
321 |
-
|
|
|
322 |
|
323 |
-
|
324 |
-
|
325 |
-
if signature_line:
|
326 |
-
st.info(f"📝 {signature_line.strip()}")
|
327 |
-
|
328 |
-
# Case 2: Document without signatures
|
329 |
-
else:
|
330 |
-
if metadata_valid and content_consistent:
|
331 |
-
st.success("✅ Document Status: VALID DOCUMENT")
|
332 |
-
st.markdown("- ✓ Valid PDF structure")
|
333 |
-
st.markdown("- ✓ Content integrity verified")
|
334 |
-
st.markdown("- ✓ No tampering indicators found")
|
335 |
-
st.markdown("- ℹ️ No signature information found (this is not an error)")
|
336 |
-
else:
|
337 |
-
st.warning("⚠️ Document Status: POTENTIALLY MODIFIED")
|
338 |
-
st.markdown("- ✓ Valid PDF structure")
|
339 |
-
st.markdown("- ❌ Some integrity checks failed")
|
340 |
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import os
|
2 |
import time
|
3 |
import hashlib
|
4 |
+
import json
|
5 |
from dotenv import load_dotenv
|
6 |
import streamlit as st
|
7 |
from langchain_community.vectorstores import FAISS
|
|
|
75 |
max-width: 250px;
|
76 |
height: auto;
|
77 |
}
|
78 |
+
.judge-badge {
|
79 |
+
background-color: #991b1b;
|
80 |
+
color: white;
|
81 |
+
padding: 5px 10px;
|
82 |
+
border-radius: 12px;
|
83 |
+
font-weight: 600;
|
84 |
+
display: inline-block;
|
85 |
+
margin-bottom: 10px;
|
86 |
+
}
|
87 |
+
.judgment-card {
|
88 |
+
background-color: #1e293b;
|
89 |
+
border-radius: 8px;
|
90 |
+
padding: 20px;
|
91 |
+
margin-bottom: 20px;
|
92 |
+
border-left: 4px solid #991b1b;
|
93 |
+
}
|
94 |
</style>
|
95 |
""", unsafe_allow_html=True)
|
96 |
|
|
|
107 |
|
108 |
if st.session_state.role is None:
|
109 |
st.markdown("<h2 style='text-align: center;'>Who are you?</h2>", unsafe_allow_html=True)
|
110 |
+
col1, col2, col3 = st.columns([1, 3, 1])
|
111 |
with col2:
|
112 |
+
col_a, col_b, col_c = st.columns(3)
|
113 |
with col_a:
|
114 |
if st.button("🧑 I am a Civilian"):
|
115 |
st.session_state.role = "civilian"
|
|
|
119 |
if st.button("⚖️ I am a Court Stakeholder"):
|
120 |
st.session_state.role = "stakeholder"
|
121 |
st.rerun()
|
122 |
+
with col_c:
|
123 |
+
if st.button("👨⚖️ I am a Judge"):
|
124 |
+
st.session_state.role = "judge"
|
125 |
+
st.rerun()
|
126 |
|
127 |
+
if (st.session_state.role == "stakeholder" or st.session_state.role == "judge") and not st.session_state.authenticated:
|
128 |
+
st.markdown(f"### 🔐 {'Judge' if st.session_state.role == 'judge' else 'Stakeholder'} Login")
|
129 |
username = st.text_input("Username")
|
130 |
password = st.text_input("Password", type="password")
|
131 |
if st.button("Login"):
|
|
|
143 |
st.rerun()
|
144 |
|
145 |
tabs = ["📘 LawGPT"]
|
146 |
+
|
147 |
+
if st.session_state.role == "judge":
|
148 |
+
tabs.extend(["👨⚖️ Judge Console", "📜 Previous Judgments"])
|
149 |
+
elif st.session_state.role == "stakeholder":
|
150 |
tabs.extend(["📝 Document Signer", "🔍 Verify Document"])
|
151 |
|
152 |
selected_tab = st.tabs(tabs)
|
153 |
|
154 |
+
# Load embeddings and DB for all roles
|
155 |
+
embeddings = HuggingFaceEmbeddings(
|
156 |
+
model_name="nomic-ai/nomic-embed-text-v1",
|
157 |
+
model_kwargs={"trust_remote_code": True, "revision": "289f532e14dbbbd5a04753fa58739e9ba766f3c7"}
|
158 |
+
)
|
159 |
+
|
160 |
+
db = FAISS.load_local("ipc_vector_db", embeddings, allow_dangerous_deserialization=True)
|
161 |
+
db_retriever = db.as_retriever(search_type="similarity", search_kwargs={"k": 4})
|
162 |
+
|
163 |
+
# Common LLM setup
|
164 |
+
llm = Together(
|
165 |
+
model="mistralai/Mistral-7B-Instruct-v0.2",
|
166 |
+
temperature=0.5,
|
167 |
+
max_tokens=1024,
|
168 |
+
together_api_key=os.getenv("TOGETHER_API_KEY")
|
169 |
+
)
|
170 |
+
|
171 |
+
# LawGPT Tab for all roles
|
172 |
if "📘 LawGPT" in tabs:
|
173 |
with selected_tab[0]:
|
174 |
st.markdown("## 💬 Your Legal AI Lawyer")
|
175 |
st.markdown("### Ask any legal question related to the Indian Penal Code (IPC)")
|
176 |
+
st.markdown("Questions might be of types like: Suppose a 16 year old is drinking and driving, and hit a pedestrian on the road. What are the possible case laws imposed and give any one previous court decisions on the same.")
|
177 |
|
178 |
def reset_conversation():
|
179 |
st.session_state.messages = []
|
|
|
186 |
k=2, memory_key="chat_history", return_messages=True
|
187 |
)
|
188 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
189 |
prompt_template = """<s>[INST]You are a legal chatbot that answers questions about the Indian Penal Code (IPC).
|
190 |
Provide clear, concise, and accurate responses based on context and user's question.
|
191 |
Avoid extra details or assumptions. Focus only on legal information.
|
|
|
202 |
input_variables=["context", "question", "chat_history"]
|
203 |
)
|
204 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
205 |
qa = ConversationalRetrievalChain.from_llm(
|
206 |
llm=llm,
|
207 |
memory=st.session_state.memory,
|
|
|
233 |
full_response += chunk
|
234 |
time.sleep(0.02)
|
235 |
message_placeholder.markdown(full_response + " ▌")
|
236 |
+
message_placeholder.markdown(full_response)
|
237 |
st.session_state.messages.append({"role": "assistant", "content": result["answer"]})
|
238 |
|
239 |
st.button("🔄 Reset Chat", on_click=reset_conversation)
|
240 |
|
241 |
+
# Judge Console Tab
|
242 |
+
if st.session_state.role == "judge":
|
243 |
+
# Initialize judgment storage
|
244 |
+
if "judgments" not in st.session_state:
|
245 |
+
st.session_state.judgments = []
|
246 |
+
|
247 |
+
# Load existing judgments if file exists
|
248 |
+
try:
|
249 |
+
with open("judgments.json", "r") as f:
|
250 |
+
st.session_state.judgments = json.load(f)
|
251 |
+
except (FileNotFoundError, json.JSONDecodeError):
|
252 |
+
pass
|
253 |
+
|
254 |
+
with selected_tab[1]:
|
255 |
+
st.markdown("## 👨⚖️ Judge's Decision Console")
|
256 |
+
st.markdown("### Enter case details for analysis and judgment")
|
257 |
+
|
258 |
+
# Input fields for case details
|
259 |
+
st.subheader("Case Information")
|
260 |
+
case_number = st.text_input("Case Number/ID")
|
261 |
+
case_title = st.text_input("Case Title")
|
262 |
+
plaintiff = st.text_input("Plaintiff/Prosecution")
|
263 |
+
defendant = st.text_input("Defendant/Accused")
|
264 |
+
|
265 |
+
# Case facts and context
|
266 |
+
st.subheader("Case Details")
|
267 |
+
case_facts = st.text_area("Enter detailed facts of the case:", height=200)
|
268 |
+
|
269 |
+
# Get relevant laws/sections that apply
|
270 |
+
relevant_laws = st.text_area("Relevant IPC Sections (if known):",
|
271 |
+
placeholder="e.g. Section 302, Section 376, etc.")
|
272 |
+
|
273 |
+
col1, col2 = st.columns(2)
|
274 |
+
with col1:
|
275 |
+
case_type = st.selectbox("Case Type", [
|
276 |
+
"Criminal", "Civil", "Family", "Property", "Cyber Crime",
|
277 |
+
"Corporate", "Intellectual Property", "Other"
|
278 |
+
])
|
279 |
+
with col2:
|
280 |
+
case_priority = st.select_slider("Case Priority",
|
281 |
+
options=["Low", "Medium", "High", "Urgent"])
|
282 |
+
|
283 |
+
if st.button("Generate Judgment"):
|
284 |
+
if not case_facts:
|
285 |
+
st.error("Please enter the case facts to generate a judgment.")
|
286 |
+
else:
|
287 |
+
with st.status("Analyzing case and formulating judgment...", expanded=True):
|
288 |
+
# Create a prompt for legal judgment
|
289 |
+
judge_prompt_template = """<s>[INST]You are an experienced Indian judge making a legal judgment based on the Indian Penal Code (IPC).
|
290 |
+
Review the case details and provide a comprehensive legal judgment.
|
291 |
+
|
292 |
+
CASE NUMBER: {case_number}
|
293 |
+
CASE TITLE: {case_title}
|
294 |
+
PLAINTIFF/PROSECUTION: {plaintiff}
|
295 |
+
DEFENDANT/ACCUSED: {defendant}
|
296 |
+
CASE TYPE: {case_type}
|
297 |
+
CASE FACTS: {case_facts}
|
298 |
+
RELEVANT IPC SECTIONS: {relevant_laws}
|
299 |
+
|
300 |
+
Your judgment should follow this structure:
|
301 |
+
1. Summary of the case
|
302 |
+
2. Facts of the case
|
303 |
+
3. Legal issues involved
|
304 |
+
4. Analysis of applicable laws and precedents
|
305 |
+
5. Reasoning and findings
|
306 |
+
6. Final judgment and orders
|
307 |
+
7. Any remedies or penalties imposed
|
308 |
+
|
309 |
+
Be impartial, consider only facts and relevant laws, and make a fair judgment.
|
310 |
+
</s>[INST]"""
|
311 |
+
|
312 |
+
judge_prompt = PromptTemplate(
|
313 |
+
template=judge_prompt_template,
|
314 |
+
input_variables=["case_number", "case_title", "plaintiff", "defendant",
|
315 |
+
"case_type", "case_facts", "relevant_laws"]
|
316 |
+
)
|
317 |
+
|
318 |
+
# Format prompt with case details
|
319 |
+
formatted_prompt = judge_prompt.format(
|
320 |
+
case_number=case_number if case_number else "Unassigned",
|
321 |
+
case_title=case_title if case_title else "Unnamed Case",
|
322 |
+
plaintiff=plaintiff if plaintiff else "Unspecified",
|
323 |
+
defendant=defendant if defendant else "Unspecified",
|
324 |
+
case_type=case_type,
|
325 |
+
case_facts=case_facts,
|
326 |
+
relevant_laws=relevant_laws if relevant_laws else "To be determined"
|
327 |
+
)
|
328 |
+
|
329 |
+
# Generate judgment using LLM
|
330 |
+
judgment_result = llm.invoke(formatted_prompt)
|
331 |
+
|
332 |
+
# Save judgment to session state
|
333 |
+
timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
|
334 |
+
judgment_data = {
|
335 |
+
"id": hashlib.md5(f"{case_title}{timestamp}".encode()).hexdigest()[:8],
|
336 |
+
"case_number": case_number if case_number else "Unassigned",
|
337 |
+
"case_title": case_title if case_title else "Unnamed Case",
|
338 |
+
"plaintiff": plaintiff,
|
339 |
+
"defendant": defendant,
|
340 |
+
"case_type": case_type,
|
341 |
+
"priority": case_priority,
|
342 |
+
"facts": case_facts,
|
343 |
+
"relevant_laws": relevant_laws,
|
344 |
+
"judgment": judgment_result,
|
345 |
+
"timestamp": timestamp
|
346 |
+
}
|
347 |
+
|
348 |
+
st.session_state.judgments.append(judgment_data)
|
349 |
+
|
350 |
+
# Save judgments to file
|
351 |
+
with open("judgments.json", "w") as f:
|
352 |
+
json.dump(st.session_state.judgments, f)
|
353 |
+
|
354 |
+
# Display the judgment
|
355 |
+
st.markdown("### Judgment Generated")
|
356 |
+
with st.container():
|
357 |
+
st.markdown(f"<div class='judgment-card'>", unsafe_allow_html=True)
|
358 |
+
st.markdown(f"<div class='judge-badge'>JUDGMENT #{judgment_data['id']}</div>", unsafe_allow_html=True)
|
359 |
+
st.markdown(f"**Case**: {judgment_data['case_title']}")
|
360 |
+
st.markdown(f"**Date**: {judgment_data['timestamp']}")
|
361 |
+
st.markdown("---")
|
362 |
+
st.markdown(judgment_data['judgment'])
|
363 |
+
st.markdown("</div>", unsafe_allow_html=True)
|
364 |
+
|
365 |
+
# Download judgment as PDF
|
366 |
+
if st.button("📥 Download Judgment as PDF"):
|
367 |
+
# Generate PDF with ReportLab
|
368 |
+
pdf_buffer = BytesIO()
|
369 |
+
c = canvas.Canvas(pdf_buffer, pagesize=letter)
|
370 |
+
width, height = letter
|
371 |
+
|
372 |
+
# Header
|
373 |
+
c.setFont("Helvetica-Bold", 16)
|
374 |
+
c.drawString(72, height - 72, f"JUDGMENT #{judgment_data['id']}")
|
375 |
+
|
376 |
+
# Case details
|
377 |
+
c.setFont("Helvetica-Bold", 12)
|
378 |
+
c.drawString(72, height - 100, f"Case: {judgment_data['case_title']}")
|
379 |
+
c.setFont("Helvetica", 10)
|
380 |
+
c.drawString(72, height - 115, f"Case Number: {judgment_data['case_number']}")
|
381 |
+
c.drawString(72, height - 130, f"Date: {judgment_data['timestamp']}")
|
382 |
+
c.drawString(72, height - 145, f"Plaintiff/Prosecution: {judgment_data['plaintiff']}")
|
383 |
+
c.drawString(72, height - 160, f"Defendant/Accused: {judgment_data['defendant']}")
|
384 |
+
c.drawString(72, height - 175, f"Case Type: {judgment_data['case_type']}")
|
385 |
+
|
386 |
+
# Line separator
|
387 |
+
c.line(72, height - 190, width - 72, height - 190)
|
388 |
+
|
389 |
+
# Format judgment text
|
390 |
+
judgment_text = judgment_data['judgment']
|
391 |
+
text_object = c.beginText(72, height - 210)
|
392 |
+
text_object.setFont("Times-Roman", 10)
|
393 |
+
|
394 |
+
# Wrap text to fit page
|
395 |
+
lines = []
|
396 |
+
for paragraph in judgment_text.split('\n\n'):
|
397 |
+
# Replace single newlines with spaces for proper wrapping
|
398 |
+
paragraph = paragraph.replace('\n', ' ')
|
399 |
+
|
400 |
+
# Simple word wrap
|
401 |
+
words = paragraph.split()
|
402 |
+
line = ''
|
403 |
+
for word in words:
|
404 |
+
if len(line + ' ' + word) <= 90: # character limit per line
|
405 |
+
line += ' ' + word if line else word
|
406 |
+
else:
|
407 |
+
lines.append(line)
|
408 |
+
line = word
|
409 |
+
if line:
|
410 |
+
lines.append(line)
|
411 |
+
|
412 |
+
# Add blank line between paragraphs
|
413 |
+
lines.append('')
|
414 |
+
|
415 |
+
# Add lines to text object with pagination
|
416 |
+
line_height = 12
|
417 |
+
lines_per_page = 50
|
418 |
+
current_line = 0
|
419 |
+
|
420 |
+
for line in lines:
|
421 |
+
if current_line >= lines_per_page:
|
422 |
+
c.drawText(text_object)
|
423 |
+
c.showPage()
|
424 |
+
text_object = c.beginText(72, height - 72)
|
425 |
+
text_object.setFont("Times-Roman", 10)
|
426 |
+
current_line = 0
|
427 |
+
|
428 |
+
text_object.textLine(line)
|
429 |
+
current_line += 1
|
430 |
+
|
431 |
+
c.drawText(text_object)
|
432 |
+
|
433 |
+
# Add footer with page numbers
|
434 |
+
c.saveState()
|
435 |
+
c.setFont("Helvetica", 8)
|
436 |
+
c.drawString(width/2 - 40, 30, f"Generated by LawGPT Judge")
|
437 |
+
c.restoreState()
|
438 |
+
|
439 |
+
# Final page with signature
|
440 |
+
c.showPage()
|
441 |
+
c.setFont("Helvetica-Bold", 12)
|
442 |
+
c.drawString(72, height - 100, "OFFICIAL JUDGMENT")
|
443 |
+
c.setFont("Helvetica", 10)
|
444 |
+
c.drawString(72, height - 130, f"Case #{judgment_data['id']} - {judgment_data['case_title']}")
|
445 |
+
c.drawString(72, height - 150, f"Date: {judgment_data['timestamp']}")
|
446 |
+
|
447 |
+
# Add barcode for authenticity
|
448 |
+
barcode = code128.Code128(f"JUDGMENT-{judgment_data['id']}", barHeight=10 * mm, barWidth=0.4)
|
449 |
+
barcode.drawOn(c, 72, 100)
|
450 |
+
|
451 |
+
# Add signature line
|
452 |
+
c.line(width - 200, 70, width - 72, 70)
|
453 |
+
c.drawString(width - 180, 60, "Judge's Signature")
|
454 |
+
|
455 |
+
c.save()
|
456 |
+
|
457 |
+
pdf_buffer.seek(0)
|
458 |
+
|
459 |
+
# Offer download
|
460 |
+
st.download_button(
|
461 |
+
label="📥 Download Generated PDF",
|
462 |
+
data=pdf_buffer,
|
463 |
+
file_name=f"judgment_{judgment_data['id']}_{judgment_data['case_title'].replace(' ', '_')}.pdf",
|
464 |
+
mime="application/pdf"
|
465 |
+
)
|
466 |
+
|
467 |
+
st.success("Judgment has been saved to the system.")
|
468 |
+
|
469 |
+
# Previous Judgments Tab
|
470 |
+
with selected_tab[2]:
|
471 |
+
st.markdown("## 📜 Previous Judgments")
|
472 |
+
st.markdown("### Review and search past judgments")
|
473 |
+
|
474 |
+
# Search and filter
|
475 |
+
search_term = st.text_input("Search judgments:", placeholder="Enter case title, number, plaintiff, etc.")
|
476 |
+
|
477 |
+
col1, col2 = st.columns(2)
|
478 |
+
with col1:
|
479 |
+
filter_type = st.multiselect("Filter by case type:",
|
480 |
+
options=["All"] + ["Criminal", "Civil", "Family", "Property", "Cyber Crime",
|
481 |
+
"Corporate", "Intellectual Property", "Other"],
|
482 |
+
default=["All"])
|
483 |
+
with col2:
|
484 |
+
sort_by = st.selectbox("Sort by:", options=["Most recent", "Oldest first", "Case title (A-Z)"])
|
485 |
+
|
486 |
+
# Display judgments based on filters
|
487 |
+
if len(st.session_state.judgments) == 0:
|
488 |
+
st.info("No judgments recorded yet. Use the Judge Console to create judgments.")
|
489 |
+
else:
|
490 |
+
# Filter judgments
|
491 |
+
filtered_judgments = st.session_state.judgments
|
492 |
+
|
493 |
+
# Apply search term filter
|
494 |
+
if search_term:
|
495 |
+
filtered_judgments = [j for j in filtered_judgments if
|
496 |
+
search_term.lower() in j['case_title'].lower() or
|
497 |
+
search_term.lower() in j['case_number'].lower() or
|
498 |
+
search_term.lower() in j['plaintiff'].lower() or
|
499 |
+
search_term.lower() in j['defendant'].lower() or
|
500 |
+
search_term.lower() in j.get('relevant_laws', '').lower()]
|
501 |
+
|
502 |
+
# Apply case type filter
|
503 |
+
if "All" not in filter_type:
|
504 |
+
filtered_judgments = [j for j in filtered_judgments if j['case_type'] in filter_type]
|
505 |
+
|
506 |
+
# Apply sorting
|
507 |
+
if sort_by == "Most recent":
|
508 |
+
filtered_judgments = sorted(filtered_judgments, key=lambda x: x['timestamp'], reverse=True)
|
509 |
+
elif sort_by == "Oldest first":
|
510 |
+
filtered_judgments = sorted(filtered_judgments, key=lambda x: x['timestamp'])
|
511 |
+
elif sort_by == "Case title (A-Z)":
|
512 |
+
filtered_judgments = sorted(filtered_judgments, key=lambda x: x['case_title'])
|
513 |
+
|
514 |
+
# Display judgments
|
515 |
+
for judgment in filtered_judgments:
|
516 |
+
with st.expander(f"**{judgment['case_title']}** - {judgment['timestamp']}"):
|
517 |
+
st.markdown(f"<div class='judge-badge'>JUDGMENT #{judgment['id']}</div>", unsafe_allow_html=True)
|
518 |
+
st.markdown(f"**Case Number**: {judgment['case_number']}")
|
519 |
+
st.markdown(f"**Plaintiff**: {judgment['plaintiff']}")
|
520 |
+
st.markdown(f"**Defendant**: {judgment['defendant']}")
|
521 |
+
st.markdown(f"**Case Type**: {judgment['case_type']} (Priority: {judgment['priority']})")
|
522 |
+
|
523 |
+
st.markdown("#### Case Facts")
|
524 |
+
st.markdown(judgment['facts'])
|
525 |
+
|
526 |
+
if judgment.get('relevant_laws'):
|
527 |
+
st.markdown("#### Relevant Laws Applied")
|
528 |
+
st.markdown(judgment['relevant_laws'])
|
529 |
+
|
530 |
+
st.markdown("#### Full Judgment")
|
531 |
+
st.markdown("---")
|
532 |
+
st.markdown(judgment['judgment'])
|
533 |
+
|
534 |
+
# Button to download individual judgment as PDF
|
535 |
+
if st.button(f"📥 Download PDF", key=f"download_{judgment['id']}"):
|
536 |
+
# Generate PDF with ReportLab
|
537 |
+
pdf_buffer = BytesIO()
|
538 |
+
c = canvas.Canvas(pdf_buffer, pagesize=letter)
|
539 |
+
width, height = letter
|
540 |
+
|
541 |
+
# Header
|
542 |
+
c.setFont("Helvetica-Bold", 16)
|
543 |
+
c.drawString(72, height - 72, f"JUDGMENT #{judgment['id']}")
|
544 |
+
|
545 |
+
# Case details
|
546 |
+
c.setFont("Helvetica-Bold", 12)
|
547 |
+
c.drawString(72, height - 100, f"Case: {judgment['case_title']}")
|
548 |
+
c.setFont("Helvetica", 10)
|
549 |
+
c.drawString(72, height - 115, f"Case Number: {judgment['case_number']}")
|
550 |
+
c.drawString(72, height - 130, f"Date: {judgment['timestamp']}")
|
551 |
+
c.drawString(72, height - 145, f"Plaintiff/Prosecution: {judgment['plaintiff']}")
|
552 |
+
c.drawString(72, height - 160, f"Defendant/Accused: {judgment['defendant']}")
|
553 |
+
c.drawString(72, height - 175, f"Case Type: {judgment['case_type']}")
|
554 |
+
|
555 |
+
# Line separator
|
556 |
+
c.line(72, height - 190, width - 72, height - 190)
|
557 |
+
|
558 |
+
# Format judgment text
|
559 |
+
judgment_text = judgment['judgment']
|
560 |
+
text_object = c.beginText(72, height - 210)
|
561 |
+
text_object.setFont("Times-Roman", 10)
|
562 |
+
|
563 |
+
# Wrap text to fit page
|
564 |
+
lines = []
|
565 |
+
for paragraph in judgment_text.split('\n\n'):
|
566 |
+
# Replace single newlines with spaces for proper wrapping
|
567 |
+
paragraph = paragraph.replace('\n', ' ')
|
568 |
+
|
569 |
+
# Simple word wrap
|
570 |
+
words = paragraph.split()
|
571 |
+
line = ''
|
572 |
+
for word in words:
|
573 |
+
if len(line + ' ' + word) <= 90: # character limit per line
|
574 |
+
line += ' ' + word if line else word
|
575 |
+
else:
|
576 |
+
lines.append(line)
|
577 |
+
line = word
|
578 |
+
if line:
|
579 |
+
lines.append(line)
|
580 |
+
|
581 |
+
# Add blank line between paragraphs
|
582 |
+
lines.append('')
|
583 |
+
|
584 |
+
# Add lines to text object with pagination
|
585 |
+
line_height = 12
|
586 |
+
lines_per_page = 50
|
587 |
+
current_line = 0
|
588 |
+
|
589 |
+
for line in lines:
|
590 |
+
if current_line >= lines_per_page:
|
591 |
+
c.drawText(text_object)
|
592 |
+
c.showPage()
|
593 |
+
text_object = c.beginText(72, height - 72)
|
594 |
+
text_object.setFont("Times-Roman", 10)
|
595 |
+
current_line = 0
|
596 |
+
|
597 |
+
text_object.textLine(line)
|
598 |
+
current_line += 1
|
599 |
+
|
600 |
+
c.drawText(text_object)
|
601 |
+
|
602 |
+
# Add footer with page numbers
|
603 |
+
c.saveState()
|
604 |
+
c.setFont("Helvetica", 8)
|
605 |
+
c.drawString(width/2 - 40, 30, f"Generated by LawGPT Judge")
|
606 |
+
c.restoreState()
|
607 |
+
|
608 |
+
# Final page with signature
|
609 |
+
c.showPage()
|
610 |
+
c.setFont("Helvetica-Bold", 12)
|
611 |
+
c.drawString(72, height - 100, "OFFICIAL JUDGMENT")
|
612 |
+
c.setFont("Helvetica", 10)
|
613 |
+
c.drawString(72, height - 130, f"Case #{judgment['id']} - {judgment['case_title']}")
|
614 |
+
c.drawString(72, height - 150, f"Date: {judgment['timestamp']}")
|
615 |
+
|
616 |
+
# Add barcode for authenticity
|
617 |
+
barcode = code128.Code128(f"JUDGMENT-{judgment['id']}", barHeight=10 * mm, barWidth=0.4)
|
618 |
+
barcode.drawOn(c, 72, 100)
|
619 |
+
|
620 |
+
# Add signature line
|
621 |
+
c.line(width - 200, 70, width - 72, 70)
|
622 |
+
c.drawString(width - 180, 60, "Judge's Signature")
|
623 |
+
|
624 |
+
c.save()
|
625 |
+
|
626 |
+
pdf_buffer.seek(0)
|
627 |
+
|
628 |
+
# Offer download
|
629 |
+
st.download_button(
|
630 |
+
label="📥 Download Generated PDF",
|
631 |
+
data=pdf_buffer,
|
632 |
+
file_name=f"judgment_{judgment['id']}_{judgment['case_title'].replace(' ', '_')}.pdf",
|
633 |
+
mime="application/pdf",
|
634 |
+
key=f"pdf_{judgment['id']}"
|
635 |
+
)
|
636 |
+
|
637 |
+
# Stakeholder tabs
|
638 |
if st.session_state.role == "stakeholder":
|
639 |
if "📝 Document Signer" in tabs:
|
640 |
with selected_tab[1]:
|
|
|
674 |
with selected_tab[2]:
|
675 |
st.markdown("## 🔍 Verify Document Authentication")
|
676 |
st.markdown("Upload any document to verify its integrity and authenticity.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
677 |
|
678 |
+
verify_file = st.file_uploader("Upload PDF for verification", type=["pdf"], key="verify")
|
|
|
679 |
|
680 |
+
if verify_file:
|
681 |
+
content = verify_file.read()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
682 |
|
683 |
+
try:
|
684 |
+
# Basic PDF validation
|
685 |
+
pdf = PdfReader(BytesIO(content))
|
686 |
+
|
687 |
+
# Extract text to look for signature markers
|
688 |
+
all_text = ""
|
689 |
+
for page in pdf.pages:
|
690 |
+
all_text += page.extract_text() or ""
|
691 |
+
|
692 |
+
# Check for digital signature information
|
693 |
+
has_signature_text = any(sig_text in all_text.lower() for sig_text in
|
694 |
+
["signed by:", "digital signature", "electronic signature"])
|
695 |
+
|
696 |
+
# Create document fingerprint/hash
|
697 |
+
doc_hash = hashlib.sha256(content).hexdigest()
|
698 |
+
|
699 |
+
# Calculate metadata integrity
|
700 |
+
metadata_valid = True
|
701 |
+
if pdf.metadata:
|
702 |
+
try:
|
703 |
+
# Check for suspicious metadata modifications
|
704 |
+
creation_date = pdf.metadata.get('/CreationDate', '')
|
705 |
+
mod_date = pdf.metadata.get('/ModDate', '')
|
706 |
+
if mod_date and creation_date:
|
707 |
+
metadata_valid = mod_date >= creation_date
|
708 |
+
except:
|
709 |
+
metadata_valid = False
|
710 |
+
|
711 |
+
# Check for content consistency
|
712 |
+
content_consistent = True
|
713 |
+
|
714 |
+
col1, col2 = st.columns(2)
|
715 |
+
|
716 |
+
with col1:
|
717 |
+
st.subheader("Document Analysis")
|
718 |
+
st.info(f"📄 Pages: {len(pdf.pages)}")
|
719 |
+
st.info(f"🔒 Contains signature markers: {'Yes' if has_signature_text else 'No'}")
|
720 |
+
|
721 |
+
# Display hash for document tracking
|
722 |
+
st.code(f"Document Hash: {doc_hash[:16]}...{doc_hash[-16:]}")
|
723 |
|
724 |
+
# Document size and characteristics
|
725 |
+
file_size = len(content) / 1024 # KB
|
726 |
+
st.info(f"📦 File size: {file_size:.2f} KB")
|
727 |
|
728 |
+
with col2:
|
729 |
+
st.subheader("Verification Results")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
730 |
|
731 |
+
# Case 1: Document has signature markers
|
732 |
+
if has_signature_text:
|
733 |
+
if metadata_valid and content_consistent:
|
734 |
+
st.success("✅ Document Status: VERIFIED AUTHENTIC")
|
735 |
+
st.markdown("- ✓ Valid PDF structure")
|
736 |
+
st.markdown("- ✓ Signature information detected")
|
737 |
+
st.markdown("- ✓ No tampering indicators found")
|
738 |
+
st.markdown("- ✓ Metadata consistency verified")
|
739 |
+
else:
|
740 |
+
st.warning("⚠️ Document Status: POTENTIALLY MODIFIED")
|
741 |
+
st.markdown("- ✓ Valid PDF structure")
|
742 |
+
st.markdown("- ✓ Signature information found")
|
743 |
+
st.markdown("- ❌ Some integrity checks failed")
|
744 |
+
|
745 |
+
if not metadata_valid:
|
746 |
+
st.markdown("- ❌ Metadata inconsistencies detected")
|
747 |
+
|
748 |
+
# Display signature extraction if present
|
749 |
+
signature_line = next((line for line in all_text.split('\n') if "signed by:" in line.lower()), "")
|
750 |
+
if signature_line:
|
751 |
+
st.info(f"📝 {signature_line.strip()}")
|
752 |
+
|
753 |
+
# Case 2: Document without signatures
|
754 |
+
else:
|
755 |
+
if metadata_valid and content_consistent:
|
756 |
+
st.success("✅ Document Status: VALID DOCUMENT")
|
757 |
+
st.markdown("- ✓ Valid PDF structure")
|
758 |
+
st.markdown("- ✓ Content integrity verified")
|
759 |
+
st.markdown("- ✓ No tampering indicators found")
|
760 |
+
st.markdown("- ℹ️ No signature information found (this is not an error)")
|
761 |
+
else:
|
762 |
+
st.warning("⚠️ Document Status: POTENTIALLY MODIFIED")
|
763 |
+
st.markdown("- ✓ Valid PDF structure")
|
764 |
+
st.markdown("- ❌ Some integrity checks failed")
|
765 |
+
|
766 |
+
if not metadata_valid:
|
767 |
+
st.markdown("- ❌ Metadata inconsistencies detected")
|
768 |
+
|
769 |
+
# Advanced options
|
770 |
+
with st.expander("🔬 Advanced Verification Details"):
|
771 |
+
st.markdown("### Document Metadata")
|
772 |
+
if pdf.metadata:
|
773 |
+
for key, value in pdf.metadata.items():
|
774 |
+
if key and value and key not in ('/CreationDate', '/ModDate'):
|
775 |
+
st.text(f"{key}: {value}")
|
776 |
+
else:
|
777 |
+
st.text("No metadata available")
|
778 |
+
|
779 |
+
st.markdown("### Integrity Timeline")
|
780 |
+
st.text(f"Creation Date: {pdf.metadata.get('/CreationDate', 'Not available')}")
|
781 |
+
st.text(f"Last Modified: {pdf.metadata.get('/ModDate', 'Not available')}")
|
782 |
+
|
783 |
+
# Additional verification for content integrity
|
784 |
+
st.markdown("### Content Analysis")
|
785 |
+
fonts_used = set()
|
786 |
+
image_count = 0
|
787 |
+
for page in pdf.pages:
|
788 |
+
if "/Font" in page["/Resources"]:
|
789 |
+
for font in page["/Resources"]["/Font"]:
|
790 |
+
fonts_used.add(str(font))
|
791 |
+
if "/XObject" in page["/Resources"]:
|
792 |
+
for obj in page["/Resources"]["/XObject"]:
|
793 |
+
if "/Subtype" in page["/Resources"]["/XObject"][obj] and \
|
794 |
+
page["/Resources"]["/XObject"][obj]["/Subtype"] == "/Image":
|
795 |
+
image_count += 1
|
796 |
+
|
797 |
+
st.text(f"Fonts detected: {len(fonts_used)}")
|
798 |
+
st.text(f"Images detected: {image_count}")
|
799 |
+
|
800 |
+
except Exception as e:
|
801 |
+
st.error(f"❌ Document Status: INVALID OR CORRUPTED")
|
802 |
+
st.markdown(f"Error: Could not process the document properly. The file may be corrupted or not a valid PDF.")
|
803 |
+
st.markdown(f"Technical details: {str(e)}")
|