Nischal Subedi
commited on
Commit
·
4c5479b
1
Parent(s):
bb08c16
added Open AI api key features
Browse files- app.py +173 -67
- requirements.txt +2 -2
app.py
CHANGED
@@ -26,7 +26,6 @@ class RAGSystem:
|
|
26 |
self.prompt_template = PromptTemplate(
|
27 |
input_variables=["query", "context", "state", "statutes"],
|
28 |
template="""You are a legal assistant specializing in tenant rights and landlord-tenant laws. Your goal is to provide accurate, detailed, and helpful answers that are explicitly grounded in the statutes provided in the context. Only use general knowledge to supplement the answer if the context lacks sufficient detail to fully answer the question, and clearly indicate when you are doing so.
|
29 |
-
|
30 |
Instructions:
|
31 |
- Use the context information and the provided statutes as the primary source to answer the question.
|
32 |
- Explicitly cite the relevant statute(s) (e.g., (AS § 34.03.220(a)(2))) in your answer to ground your response in the legal text.
|
@@ -35,17 +34,12 @@ Instructions:
|
|
35 |
- Provide detailed answers with practical examples or scenarios when possible.
|
36 |
- Use bullet points or numbered lists for clarity when applicable.
|
37 |
- Maintain a professional and neutral tone.
|
38 |
-
- Do not include a "Sources" section in the answer.
|
39 |
-
|
40 |
Question: {query}
|
41 |
State: {state}
|
42 |
-
|
43 |
Statutes found in context:
|
44 |
{statutes}
|
45 |
-
|
46 |
Context information:
|
47 |
{context}
|
48 |
-
|
49 |
Answer:"""
|
50 |
)
|
51 |
|
@@ -88,7 +82,6 @@ Answer:"""
|
|
88 |
if not state:
|
89 |
return {
|
90 |
"answer": "Please select a state to proceed with your query.",
|
91 |
-
"sources": [],
|
92 |
"context_used": "N/A",
|
93 |
"statutes_found": "N/A"
|
94 |
}
|
@@ -96,7 +89,6 @@ Answer:"""
|
|
96 |
if not openai_api_key:
|
97 |
return {
|
98 |
"answer": "Please provide an OpenAI API key to proceed.",
|
99 |
-
"sources": [],
|
100 |
"context_used": "N/A",
|
101 |
"statutes_found": "N/A"
|
102 |
}
|
@@ -108,7 +100,6 @@ Answer:"""
|
|
108 |
except Exception as e:
|
109 |
return {
|
110 |
"answer": f"Failed to initialize LLM with the provided API key: {str(e)}",
|
111 |
-
"sources": [],
|
112 |
"context_used": "N/A",
|
113 |
"statutes_found": "N/A"
|
114 |
}
|
@@ -118,37 +109,24 @@ Answer:"""
|
|
118 |
logging.info("Vector database query successful")
|
119 |
except Exception as e:
|
120 |
logging.error(f"Vector database query failed: {str(e)}")
|
121 |
-
|
122 |
-
|
123 |
-
"
|
124 |
-
"
|
125 |
-
"statutes_found": "N/A"
|
126 |
}
|
|
|
127 |
|
128 |
context_parts = []
|
129 |
-
sources = []
|
130 |
|
131 |
-
if results["document_results"]["documents"]:
|
132 |
for i, doc in enumerate(results["document_results"]["documents"][0]):
|
133 |
metadata = results["document_results"]["metadatas"][0][i]
|
134 |
context_parts.append(f"[{metadata['state']} - Chunk {metadata.get('chunk_id', 'N/A')}] {doc}")
|
135 |
-
sources.append({
|
136 |
-
"text": doc[:100] + "..." if len(doc) > 100 else doc,
|
137 |
-
"state": metadata["state"],
|
138 |
-
"chunk_id": str(metadata.get("chunk_id", "N/A")),
|
139 |
-
"source_file": metadata.get("source", "Unknown")
|
140 |
-
})
|
141 |
|
142 |
-
if results["state_results"]["documents"]:
|
143 |
for i, doc in enumerate(results["state_results"]["documents"][0]):
|
144 |
metadata = results["state_results"]["metadatas"][0][i]
|
145 |
context_parts.append(f"[{metadata['state']} - Summary] {doc}")
|
146 |
-
sources.append({
|
147 |
-
"text": doc[:100] + "..." if len(doc) > 100 else doc,
|
148 |
-
"state": metadata["state"],
|
149 |
-
"type": metadata.get("type", "summary"),
|
150 |
-
"source_file": "state_summary"
|
151 |
-
})
|
152 |
|
153 |
context = "\n\n---\n\n".join(context_parts) if context_parts else "No relevant context found."
|
154 |
|
@@ -156,7 +134,6 @@ Answer:"""
|
|
156 |
logging.info("No relevant context found for query")
|
157 |
return {
|
158 |
"answer": "I don't have sufficient information in my database to answer this question accurately. However, I can provide some general information about tenant rights.",
|
159 |
-
"sources": [],
|
160 |
"context_used": context,
|
161 |
"statutes_found": "N/A"
|
162 |
}
|
@@ -176,14 +153,12 @@ Answer:"""
|
|
176 |
logging.error(f"LLM processing failed: {str(e)}")
|
177 |
return {
|
178 |
"answer": "An error occurred while generating the answer. Please try again.",
|
179 |
-
"sources": sources,
|
180 |
"context_used": context,
|
181 |
"statutes_found": statutes
|
182 |
}
|
183 |
|
184 |
return {
|
185 |
"answer": answer['text'].strip(),
|
186 |
-
"sources": sources,
|
187 |
"context_used": context,
|
188 |
"statutes_found": statutes
|
189 |
}
|
@@ -209,40 +184,193 @@ Answer:"""
|
|
209 |
def gradio_interface(self) -> gr.Interface:
|
210 |
def query_interface(api_key: str, query: str, state: str) -> str:
|
211 |
if not api_key:
|
212 |
-
return "Please provide an OpenAI API key to proceed."
|
213 |
if not state:
|
214 |
-
return "Please select a state to proceed with your query."
|
215 |
result = self.process_query(query, state=state, openai_api_key=api_key)
|
216 |
-
return f"
|
217 |
|
218 |
states = self.get_states()
|
219 |
|
220 |
example_queries = [
|
221 |
-
["
|
222 |
-
["
|
223 |
-
["
|
224 |
-
["
|
225 |
-
["
|
226 |
]
|
227 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
228 |
interface = gr.Interface(
|
229 |
fn=query_interface,
|
230 |
inputs=[
|
231 |
gr.Textbox(
|
232 |
label="Enter your OpenAI API Key",
|
233 |
type="password",
|
234 |
-
placeholder="e.g., sk-abc123"
|
|
|
235 |
),
|
236 |
gr.Textbox(
|
237 |
label="Enter your question about Landlord-Tenant laws",
|
238 |
placeholder="e.g., What are the eviction rules?",
|
239 |
-
lines=
|
|
|
240 |
),
|
241 |
gr.Dropdown(
|
242 |
label="Select a state (required)",
|
243 |
choices=states,
|
244 |
value=None,
|
245 |
-
allow_custom_value=False
|
|
|
246 |
)
|
247 |
],
|
248 |
outputs=gr.Markdown(
|
@@ -252,30 +380,8 @@ Answer:"""
|
|
252 |
title="🏠 Landlord-Tenant Rights Bot",
|
253 |
description="Ask questions about tenant rights and landlord-tenant laws based on state-specific legal documents. Provide your OpenAI API key, select a state, and enter your question below. You can get an API key from [OpenAI](https://platform.openai.com/api-keys).",
|
254 |
examples=example_queries,
|
255 |
-
theme=gr.themes.
|
256 |
-
css=
|
257 |
-
.output-markdown {
|
258 |
-
background-color: #f8f9fa;
|
259 |
-
padding: 20px;
|
260 |
-
border-radius: 10px;
|
261 |
-
border: 1px solid #e0e0e0;
|
262 |
-
font-size: 16px;
|
263 |
-
line-height: 1.6;
|
264 |
-
}
|
265 |
-
.gr-button-primary {
|
266 |
-
background-color: #4a90e2;
|
267 |
-
border: none;
|
268 |
-
padding: 10px 20px;
|
269 |
-
font-weight: bold;
|
270 |
-
}
|
271 |
-
.gr-button-primary:hover {
|
272 |
-
background-color: #357abd;
|
273 |
-
}
|
274 |
-
.gr-form {
|
275 |
-
max-width: 800px;
|
276 |
-
margin: 0 auto;
|
277 |
-
}
|
278 |
-
"""
|
279 |
)
|
280 |
return interface
|
281 |
|
|
|
26 |
self.prompt_template = PromptTemplate(
|
27 |
input_variables=["query", "context", "state", "statutes"],
|
28 |
template="""You are a legal assistant specializing in tenant rights and landlord-tenant laws. Your goal is to provide accurate, detailed, and helpful answers that are explicitly grounded in the statutes provided in the context. Only use general knowledge to supplement the answer if the context lacks sufficient detail to fully answer the question, and clearly indicate when you are doing so.
|
|
|
29 |
Instructions:
|
30 |
- Use the context information and the provided statutes as the primary source to answer the question.
|
31 |
- Explicitly cite the relevant statute(s) (e.g., (AS § 34.03.220(a)(2))) in your answer to ground your response in the legal text.
|
|
|
34 |
- Provide detailed answers with practical examples or scenarios when possible.
|
35 |
- Use bullet points or numbered lists for clarity when applicable.
|
36 |
- Maintain a professional and neutral tone.
|
|
|
|
|
37 |
Question: {query}
|
38 |
State: {state}
|
|
|
39 |
Statutes found in context:
|
40 |
{statutes}
|
|
|
41 |
Context information:
|
42 |
{context}
|
|
|
43 |
Answer:"""
|
44 |
)
|
45 |
|
|
|
82 |
if not state:
|
83 |
return {
|
84 |
"answer": "Please select a state to proceed with your query.",
|
|
|
85 |
"context_used": "N/A",
|
86 |
"statutes_found": "N/A"
|
87 |
}
|
|
|
89 |
if not openai_api_key:
|
90 |
return {
|
91 |
"answer": "Please provide an OpenAI API key to proceed.",
|
|
|
92 |
"context_used": "N/A",
|
93 |
"statutes_found": "N/A"
|
94 |
}
|
|
|
100 |
except Exception as e:
|
101 |
return {
|
102 |
"answer": f"Failed to initialize LLM with the provided API key: {str(e)}",
|
|
|
103 |
"context_used": "N/A",
|
104 |
"statutes_found": "N/A"
|
105 |
}
|
|
|
109 |
logging.info("Vector database query successful")
|
110 |
except Exception as e:
|
111 |
logging.error(f"Vector database query failed: {str(e)}")
|
112 |
+
# Safeguard: Fallback to empty results if vector DB query fails
|
113 |
+
results = {
|
114 |
+
"document_results": {"documents": [[]], "metadatas": [[]]},
|
115 |
+
"state_results": {"documents": [[]], "metadatas": [[]]}
|
|
|
116 |
}
|
117 |
+
logging.info("Applied safeguard: Using empty results due to vector DB failure")
|
118 |
|
119 |
context_parts = []
|
|
|
120 |
|
121 |
+
if results["document_results"]["documents"] and results["document_results"]["documents"][0]:
|
122 |
for i, doc in enumerate(results["document_results"]["documents"][0]):
|
123 |
metadata = results["document_results"]["metadatas"][0][i]
|
124 |
context_parts.append(f"[{metadata['state']} - Chunk {metadata.get('chunk_id', 'N/A')}] {doc}")
|
|
|
|
|
|
|
|
|
|
|
|
|
125 |
|
126 |
+
if results["state_results"]["documents"] and results["state_results"]["documents"][0]:
|
127 |
for i, doc in enumerate(results["state_results"]["documents"][0]):
|
128 |
metadata = results["state_results"]["metadatas"][0][i]
|
129 |
context_parts.append(f"[{metadata['state']} - Summary] {doc}")
|
|
|
|
|
|
|
|
|
|
|
|
|
130 |
|
131 |
context = "\n\n---\n\n".join(context_parts) if context_parts else "No relevant context found."
|
132 |
|
|
|
134 |
logging.info("No relevant context found for query")
|
135 |
return {
|
136 |
"answer": "I don't have sufficient information in my database to answer this question accurately. However, I can provide some general information about tenant rights.",
|
|
|
137 |
"context_used": context,
|
138 |
"statutes_found": "N/A"
|
139 |
}
|
|
|
153 |
logging.error(f"LLM processing failed: {str(e)}")
|
154 |
return {
|
155 |
"answer": "An error occurred while generating the answer. Please try again.",
|
|
|
156 |
"context_used": context,
|
157 |
"statutes_found": statutes
|
158 |
}
|
159 |
|
160 |
return {
|
161 |
"answer": answer['text'].strip(),
|
|
|
162 |
"context_used": context,
|
163 |
"statutes_found": statutes
|
164 |
}
|
|
|
184 |
def gradio_interface(self) -> gr.Interface:
|
185 |
def query_interface(api_key: str, query: str, state: str) -> str:
|
186 |
if not api_key:
|
187 |
+
return "⚠️ **Error:** Please provide an OpenAI API key to proceed."
|
188 |
if not state:
|
189 |
+
return "⚠️ **Error:** Please select a state to proceed with your query."
|
190 |
result = self.process_query(query, state=state, openai_api_key=api_key)
|
191 |
+
return f"### Answer:\n{result['answer']}\n\n### Statutes Found:\n{result['statutes_found']}"
|
192 |
|
193 |
states = self.get_states()
|
194 |
|
195 |
example_queries = [
|
196 |
+
["What is the rent due date law?", "California"],
|
197 |
+
["What are the rules for security deposit returns?", "New York"],
|
198 |
+
["Can a landlord enter without notice?", "Texas"],
|
199 |
+
["What are the eviction notice requirements?", "Florida"],
|
200 |
+
["Are there rent control laws?", "Oregon"]
|
201 |
]
|
202 |
|
203 |
+
# Custom CSS for a modern, readable, and responsive UI
|
204 |
+
custom_css = """
|
205 |
+
/* General container styling */
|
206 |
+
.gr-form {
|
207 |
+
max-width: 900px;
|
208 |
+
margin: 0 auto;
|
209 |
+
padding: 20px;
|
210 |
+
background-color: #ffffff;
|
211 |
+
border-radius: 15px;
|
212 |
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
|
213 |
+
}
|
214 |
+
|
215 |
+
/* Title and description */
|
216 |
+
.gr-title {
|
217 |
+
font-size: 2.2em;
|
218 |
+
font-weight: bold;
|
219 |
+
color: #2c3e50;
|
220 |
+
text-align: center;
|
221 |
+
margin-bottom: 10px;
|
222 |
+
}
|
223 |
+
.gr-description {
|
224 |
+
font-size: 1.1em;
|
225 |
+
color: #7f8c8d;
|
226 |
+
text-align: center;
|
227 |
+
margin-bottom: 30px;
|
228 |
+
}
|
229 |
+
|
230 |
+
/* Input fields */
|
231 |
+
.gr-textbox, .gr-dropdown {
|
232 |
+
border: 1px solid #dcdcdc !important;
|
233 |
+
border-radius: 8px !important;
|
234 |
+
padding: 12px !important;
|
235 |
+
font-size: 1em !important;
|
236 |
+
transition: border-color 0.3s ease;
|
237 |
+
}
|
238 |
+
.gr-textbox:focus, .gr-dropdown:focus {
|
239 |
+
border-color: #3498db !important;
|
240 |
+
box-shadow: 0 0 5px rgba(52, 152, 219, 0.3) !important;
|
241 |
+
}
|
242 |
+
.gr-textbox label, .gr-dropdown label {
|
243 |
+
font-weight: 600;
|
244 |
+
color: #34495e;
|
245 |
+
margin-bottom: 8px;
|
246 |
+
}
|
247 |
+
|
248 |
+
/* Buttons */
|
249 |
+
.gr-button-primary {
|
250 |
+
background-color: #3498db !important;
|
251 |
+
border: none !important;
|
252 |
+
padding: 12px 30px !important;
|
253 |
+
font-weight: bold !important;
|
254 |
+
font-size: 1em !important;
|
255 |
+
border-radius: 8px !important;
|
256 |
+
transition: background-color 0.3s ease, transform 0.1s ease;
|
257 |
+
}
|
258 |
+
.gr-button-primary:hover {
|
259 |
+
background-color: #2980b9 !important;
|
260 |
+
transform: translateY(-2px);
|
261 |
+
}
|
262 |
+
.gr-button-secondary {
|
263 |
+
background-color: #95a5a6 !important;
|
264 |
+
border: none !important;
|
265 |
+
padding: 12px 30px !important;
|
266 |
+
font-weight: bold !important;
|
267 |
+
font-size: 1em !important;
|
268 |
+
border-radius: 8px !important;
|
269 |
+
transition: background-color 0.3s ease;
|
270 |
+
}
|
271 |
+
.gr-button-secondary:hover {
|
272 |
+
background-color: #7f8c8d !important;
|
273 |
+
}
|
274 |
+
|
275 |
+
/* Output area */
|
276 |
+
.output-markdown {
|
277 |
+
background-color: #f9f9f9 !important;
|
278 |
+
color: #2c3e50 !important; /* Dark text for readability */
|
279 |
+
padding: 25px !important;
|
280 |
+
border-radius: 10px !important;
|
281 |
+
border: 1px solid #e0e0e0 !important;
|
282 |
+
font-size: 1.1em !important;
|
283 |
+
line-height: 1.8 !important;
|
284 |
+
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
|
285 |
+
}
|
286 |
+
|
287 |
+
/* Examples section */
|
288 |
+
.gr-examples {
|
289 |
+
background-color: #ecf0f1;
|
290 |
+
padding: 15px;
|
291 |
+
border-radius: 10px;
|
292 |
+
margin-top: 20px;
|
293 |
+
}
|
294 |
+
.gr-examples table {
|
295 |
+
background-color: transparent !important;
|
296 |
+
}
|
297 |
+
|
298 |
+
/* Dark mode */
|
299 |
+
@media (prefers-color-scheme: dark) {
|
300 |
+
.gr-form {
|
301 |
+
background-color: #2c3e50;
|
302 |
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
|
303 |
+
}
|
304 |
+
.gr-title {
|
305 |
+
color: #ecf0f1;
|
306 |
+
}
|
307 |
+
.gr-description {
|
308 |
+
color: #bdc3c7;
|
309 |
+
}
|
310 |
+
.gr-textbox, .gr-dropdown {
|
311 |
+
background-color: #34495e !important;
|
312 |
+
color: #ecf0f1 !important;
|
313 |
+
border-color: #7f8c8d !important;
|
314 |
+
}
|
315 |
+
.gr-textbox label, .gr-dropdown label {
|
316 |
+
color: #ecf0f1;
|
317 |
+
}
|
318 |
+
.output-markdown {
|
319 |
+
background-color: #34495e !important;
|
320 |
+
color: #ecf0f1 !important;
|
321 |
+
border-color: #7f8c8d !important;
|
322 |
+
}
|
323 |
+
.gr-examples {
|
324 |
+
background-color: #3e5367;
|
325 |
+
}
|
326 |
+
}
|
327 |
+
|
328 |
+
/* Responsive design */
|
329 |
+
@media (max-width: 600px) {
|
330 |
+
.gr-form {
|
331 |
+
padding: 15px;
|
332 |
+
}
|
333 |
+
.gr-title {
|
334 |
+
font-size: 1.8em;
|
335 |
+
}
|
336 |
+
.gr-description {
|
337 |
+
font-size: 1em;
|
338 |
+
}
|
339 |
+
.gr-textbox, .gr-dropdown {
|
340 |
+
font-size: 0.9em !important;
|
341 |
+
}
|
342 |
+
.gr-button-primary, .gr-button-secondary {
|
343 |
+
padding: 10px 20px !important;
|
344 |
+
font-size: 0.9em !important;
|
345 |
+
}
|
346 |
+
.output-markdown {
|
347 |
+
font-size: 1em !important;
|
348 |
+
padding: 15px !important;
|
349 |
+
}
|
350 |
+
}
|
351 |
+
"""
|
352 |
+
|
353 |
interface = gr.Interface(
|
354 |
fn=query_interface,
|
355 |
inputs=[
|
356 |
gr.Textbox(
|
357 |
label="Enter your OpenAI API Key",
|
358 |
type="password",
|
359 |
+
placeholder="e.g., sk-abc123",
|
360 |
+
elem_classes="input-field"
|
361 |
),
|
362 |
gr.Textbox(
|
363 |
label="Enter your question about Landlord-Tenant laws",
|
364 |
placeholder="e.g., What are the eviction rules?",
|
365 |
+
lines=3,
|
366 |
+
elem_classes="input-field"
|
367 |
),
|
368 |
gr.Dropdown(
|
369 |
label="Select a state (required)",
|
370 |
choices=states,
|
371 |
value=None,
|
372 |
+
allow_custom_value=False,
|
373 |
+
elem_classes="input-field"
|
374 |
)
|
375 |
],
|
376 |
outputs=gr.Markdown(
|
|
|
380 |
title="🏠 Landlord-Tenant Rights Bot",
|
381 |
description="Ask questions about tenant rights and landlord-tenant laws based on state-specific legal documents. Provide your OpenAI API key, select a state, and enter your question below. You can get an API key from [OpenAI](https://platform.openai.com/api-keys).",
|
382 |
examples=example_queries,
|
383 |
+
theme=gr.themes.Default(),
|
384 |
+
css=custom_css
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
385 |
)
|
386 |
return interface
|
387 |
|
requirements.txt
CHANGED
@@ -1,7 +1,6 @@
|
|
1 |
gradio==4.44.0
|
2 |
langchain==0.3.1
|
3 |
-
langchain-openai==0.2.0
|
4 |
-
openai==1.40.0
|
5 |
chromadb==0.5.5
|
6 |
sentence-transformers==3.0.1
|
7 |
torch==2.2.2
|
@@ -12,3 +11,4 @@ pandas==2.2.2
|
|
12 |
huggingface_hub==0.23.4
|
13 |
pymupdf==1.24.9
|
14 |
langchain_community
|
|
|
|
1 |
gradio==4.44.0
|
2 |
langchain==0.3.1
|
3 |
+
langchain-openai==0.2.0
|
|
|
4 |
chromadb==0.5.5
|
5 |
sentence-transformers==3.0.1
|
6 |
torch==2.2.2
|
|
|
11 |
huggingface_hub==0.23.4
|
12 |
pymupdf==1.24.9
|
13 |
langchain_community
|
14 |
+
|