ChAbhishek28 commited on
Commit
061a93c
·
1 Parent(s): a0b0f78

Add 899999999999999

Browse files
app.py CHANGED
@@ -1,3 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import os
2
  import logging
3
  from datetime import datetime
 
1
+ from fastapi import FastAPI, Request
2
+ from fastapi.responses import FileResponse, JSONResponse
3
+ from evidence_pack_export import export_evidence_pack_pdf, export_evidence_pack_csv
4
+
5
+ app = FastAPI()
6
+
7
+ # ...existing code...
8
+
9
+ @app.post("/export_evidence_pack")
10
+ async def export_evidence_pack(request: Request):
11
+ data = await request.json()
12
+ format = data.get("format", "pdf")
13
+ if format == "pdf":
14
+ file_path = export_evidence_pack_pdf(data)
15
+ return FileResponse(file_path, media_type="application/pdf", filename="evidence_pack.pdf")
16
+ elif format == "csv":
17
+ file_path = export_evidence_pack_csv(data)
18
+ return FileResponse(file_path, media_type="text/csv", filename="evidence_pack.csv")
19
+ else:
20
+ return JSONResponse({"error": "Invalid format"}, status_code=400)
21
  import os
22
  import logging
23
  from datetime import datetime
enhanced_websocket_handler.py CHANGED
@@ -422,27 +422,41 @@ async def get_hybrid_response(user_message: str, context: str, config: dict, kno
422
  from rag_service import search_documents_async
423
  docs = await search_documents_async(user_message, limit=3)
424
  if docs:
425
- context = "\n\n".join([doc["content"] for doc in docs])
426
- sources = [doc["source"] for doc in docs]
427
- logger.info(f"📚 Found {len(docs)} documents from sources: {sources}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
428
  else:
429
  logger.info("📚 No documents found, using existing context")
 
430
  except Exception as e:
431
  logger.warning(f"❌ Document search failed: {e}, using existing context")
432
- logger.info(f"🤖 Getting LLM response (streaming)...")
433
- response_chunks = []
434
- async for chunk in hybrid_llm_service.get_streaming_response(
435
- user_message,
436
- context=context,
437
- system_prompt="""You are a helpful government document assistant. Provide accurate, helpful responses based on the context provided. When appropriate, suggest additional resources or redirect users to relevant departments for more assistance."""
438
- ):
439
- response_chunks.append(chunk)
440
- yield chunk # Yield each chunk for streaming to frontend
441
- full_response = "".join(response_chunks)
442
- logger.info(f"✅ LLM response received, length: {len(full_response)}")
443
- provider = hybrid_llm_service.choose_llm_provider(user_message)
444
- provider_used = provider.value if provider else "unknown"
445
- return
446
 
447
  async def send_text_response(websocket: WebSocket, response_text: str, provider_used: str, session_data: dict):
448
  """Send text response to client"""
 
422
  from rag_service import search_documents_async
423
  docs = await search_documents_async(user_message, limit=3)
424
  if docs:
425
+ from scenario_analysis_service import run_scenario_analysis
426
+ # Detect scenario analysis intent (simple keyword match)
427
+ scenario_keywords = ["impact", "cost", "scenario", "multiplier", "da", "dr"]
428
+ if any(kw in user_message.lower() for kw in scenario_keywords):
429
+ # Example params extraction (can be improved)
430
+ params = {
431
+ 'base_pension': 30000,
432
+ 'multiplier': 1.1 if "multiplier" in user_message.lower() else 1.0,
433
+ 'da_percent': 0.06 if "da" in user_message.lower() else 0.0,
434
+ 'num_beneficiaries': 1000,
435
+ 'years': 3,
436
+ 'inflation': 0.05
437
+ }
438
+ scenario_result = run_scenario_analysis(params)
439
+ else:
440
+ scenario_result = None
441
+ for doc in docs:
442
+ response_obj = {
443
+ "clause_text": doc.get("clause_text", ""),
444
+ "summary": doc.get("summary", ""),
445
+ "role_checklist": doc.get("role_checklist", []),
446
+ "source_title": doc.get("source_title", ""),
447
+ "clause_id": doc.get("clause_id", ""),
448
+ "date": doc.get("date", ""),
449
+ "url": doc.get("url", ""),
450
+ "score": doc.get("score", 1.0),
451
+ "scenario_analysis": scenario_result
452
+ }
453
+ yield response_obj
454
  else:
455
  logger.info("📚 No documents found, using existing context")
456
+ yield {"clause_text": context, "summary": "", "role_checklist": [], "source_title": "", "clause_id": "", "date": "", "url": "", "score": 1.0}
457
  except Exception as e:
458
  logger.warning(f"❌ Document search failed: {e}, using existing context")
459
+ yield {"clause_text": context, "summary": "", "role_checklist": [], "source_title": "", "clause_id": "", "date": "", "url": "", "score": 1.0}
 
 
 
 
 
 
 
 
 
 
 
 
 
460
 
461
  async def send_text_response(websocket: WebSocket, response_text: str, provider_used: str, session_data: dict):
462
  """Send text response to client"""
evidence_pack_export.py ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import csv
2
+ from fpdf import FPDF
3
+ import tempfile
4
+ import os
5
+
6
+ def export_evidence_pack_pdf(data, filename=None):
7
+ """
8
+ Export evidence pack as PDF. Data should include clause, summary, checklist, scenario, metadata.
9
+ Returns path to PDF file.
10
+ """
11
+ pdf = FPDF()
12
+ pdf.add_page()
13
+ pdf.set_font("Arial", size=12)
14
+ pdf.cell(200, 10, txt="Evidence Pack", ln=True, align='C')
15
+ pdf.ln(10)
16
+ pdf.set_font("Arial", size=10)
17
+ pdf.multi_cell(0, 8, f"Clause: {data.get('clause_text','')}")
18
+ pdf.multi_cell(0, 8, f"Summary: {data.get('summary','')}")
19
+ pdf.multi_cell(0, 8, f"Checklist: {', '.join(data.get('role_checklist',[]))}")
20
+ pdf.multi_cell(0, 8, f"Source: {data.get('source_title','')} | Clause ID: {data.get('clause_id','')} | Date: {data.get('date','')} | URL: {data.get('url','')}")
21
+ pdf.ln(5)
22
+ scenario = data.get('scenario_analysis',{})
23
+ if scenario:
24
+ pdf.multi_cell(0, 8, f"Scenario Analysis:")
25
+ pdf.multi_cell(0, 8, f"Yearly Results: {scenario.get('yearly_results','')}")
26
+ pdf.multi_cell(0, 8, f"Cumulative Base: {scenario.get('cumulative_base','')}")
27
+ pdf.multi_cell(0, 8, f"Cumulative Scenario: {scenario.get('cumulative_scenario','')}")
28
+ pdf.multi_cell(0, 8, f"Optimistic: {scenario.get('optimistic','')}")
29
+ pdf.multi_cell(0, 8, f"Pessimistic: {scenario.get('pessimistic','')}")
30
+ pdf.multi_cell(0, 8, f"Driver Breakdown: {scenario.get('driver_breakdown','')}")
31
+ if not filename:
32
+ filename = os.path.join(tempfile.gettempdir(), f"evidence_pack_{os.getpid()}.pdf")
33
+ pdf.output(filename)
34
+ return filename
35
+
36
+ def export_evidence_pack_csv(data, filename=None):
37
+ """
38
+ Export evidence pack as CSV. Data should include clause, summary, checklist, scenario, metadata.
39
+ Returns path to CSV file.
40
+ """
41
+ if not filename:
42
+ filename = os.path.join(tempfile.gettempdir(), f"evidence_pack_{os.getpid()}.csv")
43
+ with open(filename, 'w', newline='', encoding='utf-8') as csvfile:
44
+ writer = csv.writer(csvfile)
45
+ writer.writerow(["Field", "Value"])
46
+ writer.writerow(["Clause", data.get('clause_text','')])
47
+ writer.writerow(["Summary", data.get('summary','')])
48
+ writer.writerow(["Checklist", ', '.join(data.get('role_checklist',[]))])
49
+ writer.writerow(["Source", data.get('source_title','')])
50
+ writer.writerow(["Clause ID", data.get('clause_id','')])
51
+ writer.writerow(["Date", data.get('date','')])
52
+ writer.writerow(["URL", data.get('url','')])
53
+ scenario = data.get('scenario_analysis',{})
54
+ if scenario:
55
+ writer.writerow(["Yearly Results", scenario.get('yearly_results','')])
56
+ writer.writerow(["Cumulative Base", scenario.get('cumulative_base','')])
57
+ writer.writerow(["Cumulative Scenario", scenario.get('cumulative_scenario','')])
58
+ writer.writerow(["Optimistic", scenario.get('optimistic','')])
59
+ writer.writerow(["Pessimistic", scenario.get('pessimistic','')])
60
+ writer.writerow(["Driver Breakdown", scenario.get('driver_breakdown','')])
61
+ return filename
rag_service.py CHANGED
@@ -149,9 +149,42 @@ async def search_documents_async(query: str, limit: int = 5) -> List[Dict[str, A
149
  all_docs = sorted(all_docs, key=lambda x: getattr(x, 'score', 1.0), reverse=True)[:limit]
150
  results = []
151
  for doc in all_docs:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
  results.append({
153
- "content": doc.page_content,
154
- "source": doc.metadata.get('source', 'Unknown'),
 
 
 
 
 
155
  "score": getattr(doc, 'score', 1.0)
156
  })
157
  logger.info(f"📚 Found {len(results)} documents for query: {query}")
@@ -184,9 +217,39 @@ async def search_rajasthan_documents_async(query: str, limit: int = 5) -> List[D
184
  return get_fallback_content(query)
185
  results = []
186
  for _, row in search_results.iterrows():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
187
  results.append({
188
- "content": row['content'],
189
- "source": row['filename'],
 
 
 
 
 
190
  "score": float(row.get('_distance', 1.0))
191
  })
192
  logger.info(f"📚 Found {len(results)} Rajasthan documents for query: {query}")
 
149
  all_docs = sorted(all_docs, key=lambda x: getattr(x, 'score', 1.0), reverse=True)[:limit]
150
  results = []
151
  for doc in all_docs:
152
+ metadata = doc.metadata if hasattr(doc, 'metadata') else {}
153
+ clause_text = doc.page_content
154
+ # Simple extractive summary: first sentence or up to 2 lines
155
+ summary = clause_text.split(". ")[0][:180] + ("..." if len(clause_text) > 180 else "")
156
+ # Role-aware checklist logic (basic template)
157
+ role_checklist = []
158
+ query_lower = query.lower()
159
+ if "pension" in query_lower:
160
+ role_checklist = [
161
+ "Check eligibility (service years, misconduct)",
162
+ "Collect required documents (service book, ID, proof)",
163
+ "Obtain approvals (sanctioning authority)",
164
+ "Submit application to pension office"
165
+ ]
166
+ elif "procurement" in query_lower or "bid" in query_lower:
167
+ role_checklist = [
168
+ "Review procurement thresholds and MSME relaxations",
169
+ "Prepare bid documents",
170
+ "Complete registration and approvals",
171
+ "Submit bid before deadline"
172
+ ]
173
+ elif "finance" in query_lower:
174
+ role_checklist = [
175
+ "Check sanctioning steps",
176
+ "Update registers",
177
+ "Obtain necessary approvals",
178
+ "Notify stakeholders"
179
+ ]
180
  results.append({
181
+ "clause_text": clause_text,
182
+ "summary": summary,
183
+ "role_checklist": role_checklist,
184
+ "source_title": metadata.get('title', metadata.get('source', 'Unknown')),
185
+ "clause_id": metadata.get('clause_id', ''),
186
+ "date": metadata.get('date', ''),
187
+ "url": metadata.get('url', ''),
188
  "score": getattr(doc, 'score', 1.0)
189
  })
190
  logger.info(f"📚 Found {len(results)} documents for query: {query}")
 
217
  return get_fallback_content(query)
218
  results = []
219
  for _, row in search_results.iterrows():
220
+ clause_text = row['content']
221
+ summary = clause_text.split(". ")[0][:180] + ("..." if len(clause_text) > 180 else "")
222
+ role_checklist = []
223
+ query_lower = query.lower()
224
+ if "pension" in query_lower:
225
+ role_checklist = [
226
+ "Check eligibility (service years, misconduct)",
227
+ "Collect required documents (service book, ID, proof)",
228
+ "Obtain approvals (sanctioning authority)",
229
+ "Submit application to pension office"
230
+ ]
231
+ elif "procurement" in query_lower or "bid" in query_lower:
232
+ role_checklist = [
233
+ "Review procurement thresholds and MSME relaxations",
234
+ "Prepare bid documents",
235
+ "Complete registration and approvals",
236
+ "Submit bid before deadline"
237
+ ]
238
+ elif "finance" in query_lower:
239
+ role_checklist = [
240
+ "Check sanctioning steps",
241
+ "Update registers",
242
+ "Obtain necessary approvals",
243
+ "Notify stakeholders"
244
+ ]
245
  results.append({
246
+ "clause_text": clause_text,
247
+ "summary": summary,
248
+ "role_checklist": role_checklist,
249
+ "source_title": row.get('title', row.get('filename', 'Unknown')),
250
+ "clause_id": row.get('clause_id', ''),
251
+ "date": row.get('date', ''),
252
+ "url": row.get('url', ''),
253
  "score": float(row.get('_distance', 1.0))
254
  })
255
  logger.info(f"📚 Found {len(results)} Rajasthan documents for query: {query}")
scenario_analysis_service.py CHANGED
@@ -1,3 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import matplotlib.pyplot as plt
2
  import seaborn as sns
3
  import plotly.graph_objects as go
 
1
+ import math
2
+ import datetime
3
+ from typing import Dict, Any
4
+
5
+ def run_scenario_analysis(params: Dict[str, Any]) -> Dict[str, Any]:
6
+ """
7
+ Simulate scenario impact for pension/DA/DR changes.
8
+ params: {
9
+ 'base_pension': float,
10
+ 'multiplier': float,
11
+ 'da_percent': float,
12
+ 'num_beneficiaries': int,
13
+ 'years': int,
14
+ 'inflation': float
15
+ }
16
+ Returns: dict with yearly/cumulative cost, sensitivity bands, driver breakdown
17
+ """
18
+ base_pension = params.get('base_pension', 30000)
19
+ multiplier = params.get('multiplier', 1.0)
20
+ da_percent = params.get('da_percent', 0.06)
21
+ num_beneficiaries = params.get('num_beneficiaries', 1000)
22
+ years = params.get('years', 3)
23
+ inflation = params.get('inflation', 0.05)
24
+
25
+ results = []
26
+ total_base = 0
27
+ total_scenario = 0
28
+ for year in range(1, years+1):
29
+ # Baseline
30
+ base_cost = base_pension * num_beneficiaries * ((1+inflation)**(year-1))
31
+ # Scenario: multiplier and DA applied
32
+ scenario_cost = base_pension * multiplier * (1+da_percent) * num_beneficiaries * ((1+inflation)**(year-1))
33
+ results.append({
34
+ 'year': year,
35
+ 'base_cost': round(base_cost,2),
36
+ 'scenario_cost': round(scenario_cost,2)
37
+ })
38
+ total_base += base_cost
39
+ total_scenario += scenario_cost
40
+
41
+ # Sensitivity bands (simple optimistic/pessimistic)
42
+ optimistic = total_scenario * 0.95
43
+ pessimistic = total_scenario * 1.10
44
+ driver_breakdown = {
45
+ 'beneficiaries': round(num_beneficiaries * base_pension * multiplier * years,2),
46
+ 'rate_change': round(base_pension * (multiplier-1) * num_beneficiaries * years,2),
47
+ 'da_increase': round(base_pension * da_percent * num_beneficiaries * years,2)
48
+ }
49
+
50
+ return {
51
+ 'yearly_results': results,
52
+ 'cumulative_base': round(total_base,2),
53
+ 'cumulative_scenario': round(total_scenario,2),
54
+ 'optimistic': round(optimistic,2),
55
+ 'pessimistic': round(pessimistic,2),
56
+ 'driver_breakdown': driver_breakdown,
57
+ 'timestamp': datetime.datetime.now().isoformat()
58
+ }
59
  import matplotlib.pyplot as plt
60
  import seaborn as sns
61
  import plotly.graph_objects as go