GodsDevProject commited on
Commit
5183ea2
·
verified ·
1 Parent(s): 737ab27

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +197 -196
app.py CHANGED
@@ -1,18 +1,36 @@
1
  # ======================================================
2
  # Federal FOIA Intelligence Search
3
- # HF Reviewer–Safe Reference Implementation
4
  # ======================================================
5
 
6
- import time, hashlib, io, zipfile, os, tempfile, base64
7
  from datetime import datetime
8
  from urllib.parse import quote_plus
9
  import requests
10
 
11
  import gradio as gr
12
  from fastapi import FastAPI
13
- from fastapi.staticfiles import StaticFiles
14
  from fastapi.responses import JSONResponse
15
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  from reportlab.platypus import SimpleDocTemplate, Paragraph, PageBreak
17
  from reportlab.lib.styles import getSampleStyleSheet
18
  from reportlab.lib.pagesizes import LETTER
@@ -21,17 +39,15 @@ from reportlab.lib.pagesizes import LETTER
21
  # HARD GOVERNANCE FLAGS (NON-NEGOTIABLE)
22
  # ======================================================
23
 
 
24
  ENABLE_FAISS_PHASE_4 = False # REQUIRES FORMAL APPROVAL
25
- ENABLE_AI = True # USER OPT-IN ONLY
26
- ENABLE_PDF_EXTRACTION = True # USER OPT-IN ONLY
27
- ENABLE_DOC_LEVEL_APIS = False # API-ONLY (CIA/FBI WHEN PUBLISHED)
28
 
29
  # ======================================================
30
  # SESSION STATE (EPHEMERAL)
31
  # ======================================================
32
 
33
  LAST_RESULTS = []
34
- SELECTED_INDEX = None
35
  AI_APPENDIX = None
36
 
37
  # ======================================================
@@ -41,56 +57,55 @@ AI_APPENDIX = None
41
  def sha256_text(t: str) -> str:
42
  return hashlib.sha256(t.encode()).hexdigest()
43
 
44
- def citation_hash(r: dict) -> str:
45
- return hashlib.sha256(
46
- f"{r['agency']}|{r['resolved_url']}|{r['timestamp']}".encode()
47
- ).hexdigest()[:16]
48
-
49
- def provenance_headers(payload: str, ai: bool = False) -> dict:
50
- return {
51
- "Tool-Version": "1.7.0",
52
- "Generated-UTC": datetime.utcnow().isoformat(),
53
- "Content-SHA256": sha256_text(payload),
54
- "Public-Source-Only": "true",
55
- "AI-Assisted": "formatting-only" if ai else "false",
56
- "Court-Safe": "true",
57
- }
58
-
59
- def render_provenance_block(text: str, ai: bool = False) -> str:
60
- return "\n".join(
61
- f"{k}: {v}" for k, v in provenance_headers(text, ai).items()
62
- )
63
 
64
  # ======================================================
65
- # FAISS PHASE-4 (APPROVAL-GATED)
66
  # ======================================================
67
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
  class Phase4FAISS:
69
  def __init__(self):
70
  if not ENABLE_FAISS_PHASE_4:
71
  raise RuntimeError(
72
- "Phase-4 FAISS disabled pending formal approval."
73
  )
74
 
75
  # ======================================================
76
- # FOIA ADAPTERS (LINK-OUT + API STUBS)
77
  # ======================================================
78
 
79
  class FOIAAdapter:
80
  agency = "UNKNOWN"
81
  search_url = ""
82
 
83
- def search(self, query: str):
84
- start = time.time()
85
  url = self.search_url.format(q=quote_plus(query))
86
- latency = round((time.time() - start) * 1000, 1)
87
  return [{
88
  "agency": self.agency,
89
  "title": f"{self.agency} FOIA Reading Room",
90
- "url": url,
91
  "timestamp": datetime.utcnow().isoformat(),
92
- "latency_ms": latency,
93
- "sealed": False,
94
  }]
95
 
96
  class CIA(FOIAAdapter):
@@ -101,229 +116,215 @@ class FBI(FOIAAdapter):
101
  agency = "FBI"
102
  search_url = "https://vault.fbi.gov/search?SearchableText={q}"
103
 
104
- class DOJ(FOIAAdapter):
105
- agency = "DOJ"
106
- search_url = "https://www.justice.gov/foia/library?search={q}"
107
-
108
- class DHS(FOIAAdapter):
109
- agency = "DHS"
110
- search_url = "https://www.dhs.gov/foia-library/search?search={q}"
111
-
112
- class STATE(FOIAAdapter):
113
- agency = "State Department"
114
- search_url = "https://foia.state.gov/Search/Search.aspx?q={q}"
115
-
116
- class NSA(FOIAAdapter):
117
- agency = "NSA"
118
- search_url = "https://www.nsa.gov/resources/everyone/foia/reading-room/?q={q}"
119
-
120
  ALL_ADAPTERS = {
121
  "CIA": CIA(),
122
  "FBI": FBI(),
123
- "DOJ": DOJ(),
124
- "DHS": DHS(),
125
- "State": STATE(),
126
- "NSA": NSA(),
127
  }
128
 
129
  # ======================================================
130
- # PDF RESOLUTION (SAFE)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
  # ======================================================
132
 
133
- def resolve_pdf_url(url: str):
 
 
134
  try:
135
- r = requests.get(url, timeout=10, allow_redirects=True)
136
- ct = r.headers.get("content-type", "").lower()
137
- is_pdf = r.url.lower().endswith(".pdf") or "application/pdf" in ct
138
- return is_pdf, r.url
 
 
139
  except Exception:
140
- return False, url
141
 
142
  # ======================================================
143
  # SEARCH
144
  # ======================================================
145
 
146
  def run_search(query, agencies):
147
- global LAST_RESULTS, SELECTED_INDEX
148
- SELECTED_INDEX = None
149
  LAST_RESULTS = []
150
- rows = []
151
 
 
152
  for name in agencies:
153
- adapter = ALL_ADAPTERS[name]
154
- for r in adapter.search(query):
155
- r["resolved_pdf"], r["resolved_url"] = resolve_pdf_url(r["url"])
156
- r["hash"] = citation_hash(r)
 
 
 
 
 
 
 
 
157
  LAST_RESULTS.append(r)
158
- rows.append([
159
- r["agency"],
160
- r["title"],
161
- r["resolved_url"],
162
- r["hash"],
163
- f"{r['latency_ms']} ms",
164
- ])
165
 
166
- return rows, render_cards(), "No document selected"
167
 
168
  # ======================================================
169
- # ASK-AI (GOVERNANCE-GATED)
170
  # ======================================================
171
 
172
- def can_enable_ai(r):
173
- return (
174
- ENABLE_AI
175
- and r.get("resolved_pdf", False)
176
- and not r.get("sealed", False)
177
- )
178
-
179
  def ask_ai(index: int):
180
- global SELECTED_INDEX, AI_APPENDIX
181
  r = LAST_RESULTS[index]
182
- SELECTED_INDEX = index
183
 
184
- response = (
185
- f"AI ASSISTIVE SUMMARY (NON-AUTHORITATIVE)\n\n"
186
  f"Agency: {r['agency']}\n"
187
- f"Source: {r['resolved_url']}\n\n"
188
- "This AI output is generated solely to assist review of a "
189
- "public FOIA document. It makes no factual assertions "
190
- "and carries no evidentiary weight."
191
  )
192
 
193
  AI_APPENDIX = {
194
- "text": response,
195
- "hash": sha256_text(response),
196
- "provenance": render_provenance_block(response, ai=True),
197
  }
198
 
199
- return response + "\n\n" + AI_APPENDIX["provenance"]
200
 
201
  # ======================================================
202
- # RENDER CARDS
203
  # ======================================================
204
 
205
- def render_cards():
206
- cards = []
207
- for idx, r in enumerate(LAST_RESULTS):
208
- ai_ok = can_enable_ai(r)
209
- cards.append(f"""
210
- <div class="card">
211
- <div class="card-header">
212
- <strong>{r['agency']}</strong>
213
- <button class="ask-ai"
214
- onclick="askAI({idx})"
215
- {"disabled" if not ai_ok else ""}>
216
- Ask AI
217
- </button>
218
- </div>
219
- <div><b>{r['title']}</b></div>
220
- <div class="actions">
221
- <a href="{r['resolved_url']}" target="_blank">View Source</a>
222
- </div>
223
- </div>
224
- """)
225
- return "".join(cards) or "No results."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
226
 
227
  # ======================================================
228
- # COURT BUNDLE (CM/ECF-READY)
229
  # ======================================================
230
 
231
  def generate_court_bundle():
232
- ecf_no = f"ECF-PREFILE-{datetime.utcnow().strftime('%Y%m%d-%H%M%S')}"
233
  with tempfile.TemporaryDirectory() as td:
234
- zpath = os.path.join(td, "court_bundle.zip")
235
- with zipfile.ZipFile(zpath, "w") as z:
236
  for i, r in enumerate(LAST_RESULTS, 1):
237
- content = (
238
- f"{r['agency']} FOIA Reading Room\n"
239
- f"{r['resolved_url']}\n\n"
240
- f"{render_provenance_block(r['resolved_url'])}"
241
- )
242
- z.writestr(f"Exhibit_{i:03d}.txt", content)
243
- z.writestr(f"Exhibit_{i:03d}.sha256", r["hash"])
244
-
245
- if AI_APPENDIX:
246
- z.writestr("Exhibit_AI_Appendix.txt", AI_APPENDIX["text"])
247
- z.writestr("Exhibit_AI_Appendix.sha256", AI_APPENDIX["hash"])
248
  z.writestr(
249
- "Exhibit_AI_Appendix.provenance.txt",
250
- AI_APPENDIX["provenance"]
251
  )
252
- return zpath
253
 
254
- # ======================================================
255
- # FASTAPI APP
256
- # ======================================================
257
-
258
- fastapi_app = FastAPI()
259
 
260
- @fastapi_app.get("/ask_ai")
261
- def ask_ai_endpoint(index: int):
262
- return JSONResponse({"result": ask_ai(index)})
263
 
264
- if os.path.exists("governance-site"):
265
- fastapi_app.mount(
266
- "/gov",
267
- StaticFiles(directory="governance-site", html=True),
268
- name="governance",
269
- )
270
 
271
  # ======================================================
272
  # UI
273
  # ======================================================
274
 
275
- CSS = """
276
- .card { border:1px solid #333; border-radius:16px; padding:16px; margin-bottom:18px; }
277
- .card-header { display:flex; justify-content:space-between; }
278
- .ask-ai {
279
- background:#1e88e5; color:white; border:none;
280
- padding:6px 16px; border-radius:999px;
281
- }
282
- .ask-ai:disabled { background:#666; }
283
- .actions { margin-top:10px; }
284
- """
 
 
 
 
 
 
 
 
 
 
 
 
 
285
 
286
- with gr.Blocks() as gradio_ui:
287
- gr.Markdown(
288
- "## Federal FOIA Intelligence Search\n"
289
- "Public FOIA reading rooms only • Research & education use"
290
- )
291
 
292
  with gr.Tab("Search"):
293
- agencies = gr.CheckboxGroup(
294
- list(ALL_ADAPTERS.keys()),
295
- value=list(ALL_ADAPTERS.keys()),
296
- label="Agencies",
297
- )
298
- query = gr.Textbox(placeholder="Search terms")
299
- table = gr.Dataframe(
300
- headers=["Agency", "Title", "Resolved URL", "Hash", "Latency"]
301
- )
302
- gallery = gr.HTML()
303
- status = gr.Textbox(label="AI Status", lines=6)
304
- gr.Button("Search").click(
305
- run_search, [query, agencies], [table, gallery, status]
306
  )
307
-
308
- with gr.Tab("Court"):
309
  gr.Button("Generate Court Bundle").click(
310
  lambda: generate_court_bundle(),
311
  None,
312
- gr.File(),
313
  )
314
 
315
- with gr.Tab("Trust"):
316
- gr.HTML(
317
- '<iframe src="/gov/index.html" '
318
- 'style="width:100%;height:700px;border:1px solid #ccc;"></iframe>'
319
- )
320
-
321
- # ======================================================
322
- # MOUNT (HF-SAFE)
323
- # ======================================================
324
 
325
- app = gr.mount_gradio_app(
326
- fastapi_app,
327
- gradio_ui,
328
- path="/"
329
- )
 
1
  # ======================================================
2
  # Federal FOIA Intelligence Search
3
+ # HF Reviewer–Safe / Court-Safe Reference Implementation
4
  # ======================================================
5
 
6
+ import os, io, zipfile, tempfile, hashlib, base64
7
  from datetime import datetime
8
  from urllib.parse import quote_plus
9
  import requests
10
 
11
  import gradio as gr
12
  from fastapi import FastAPI
 
13
  from fastapi.responses import JSONResponse
14
 
15
+ # ======================================================
16
+ # OPTIONAL PDF SUPPORT
17
+ # ======================================================
18
+
19
+ PDF_THUMBNAILS_AVAILABLE = False
20
+ PDF_TEXT_EXTRACTION_AVAILABLE = False
21
+
22
+ try:
23
+ from pdf2image import convert_from_bytes
24
+ PDF_THUMBNAILS_AVAILABLE = True
25
+ except Exception:
26
+ pass
27
+
28
+ try:
29
+ from PyPDF2 import PdfReader
30
+ PDF_TEXT_EXTRACTION_AVAILABLE = True
31
+ except Exception:
32
+ pass
33
+
34
  from reportlab.platypus import SimpleDocTemplate, Paragraph, PageBreak
35
  from reportlab.lib.styles import getSampleStyleSheet
36
  from reportlab.lib.pagesizes import LETTER
 
39
  # HARD GOVERNANCE FLAGS (NON-NEGOTIABLE)
40
  # ======================================================
41
 
42
+ ENABLE_AI = True
43
  ENABLE_FAISS_PHASE_4 = False # REQUIRES FORMAL APPROVAL
44
+ ENABLE_DOC_LEVEL_APIS = False
 
 
45
 
46
  # ======================================================
47
  # SESSION STATE (EPHEMERAL)
48
  # ======================================================
49
 
50
  LAST_RESULTS = []
 
51
  AI_APPENDIX = None
52
 
53
  # ======================================================
 
57
  def sha256_text(t: str) -> str:
58
  return hashlib.sha256(t.encode()).hexdigest()
59
 
60
+ def provenance_block(payload: str, ai=False) -> str:
61
+ return "\n".join([
62
+ "Tool-Version: 1.9.0",
63
+ f"Generated-UTC: {datetime.utcnow().isoformat()}",
64
+ f"Content-SHA256: {sha256_text(payload)}",
65
+ "Public-Source-Only: true",
66
+ f"AI-Assisted: {'true' if ai else 'false'}",
67
+ "Court-Safe: true",
68
+ ])
 
 
 
 
 
 
 
 
 
 
69
 
70
  # ======================================================
71
+ # FAISS PHASE-4 FORMAL APPROVAL WORKFLOW
72
  # ======================================================
73
 
74
+ FAISS_APPROVAL_MEMO = """
75
+ Phase-4 FAISS Approval Workflow
76
+
77
+ 1. Written authorization from data-owning agency
78
+ 2. Judicial approval (if court-adjacent use)
79
+ 3. Privacy Impact Assessment (PIA)
80
+ 4. Security review (no embeddings of restricted data)
81
+ 5. ENABLE_FAISS_PHASE_4 flag set to True
82
+ 6. Signed change record archived
83
+
84
+ Status: NOT APPROVED
85
+ """
86
+
87
  class Phase4FAISS:
88
  def __init__(self):
89
  if not ENABLE_FAISS_PHASE_4:
90
  raise RuntimeError(
91
+ "Phase-4 FAISS indexing is disabled pending formal approval."
92
  )
93
 
94
  # ======================================================
95
+ # FOIA ADAPTERS (LINK-OUT ONLY)
96
  # ======================================================
97
 
98
  class FOIAAdapter:
99
  agency = "UNKNOWN"
100
  search_url = ""
101
 
102
+ def search(self, query):
 
103
  url = self.search_url.format(q=quote_plus(query))
 
104
  return [{
105
  "agency": self.agency,
106
  "title": f"{self.agency} FOIA Reading Room",
107
+ "resolved_url": url,
108
  "timestamp": datetime.utcnow().isoformat(),
 
 
109
  }]
110
 
111
  class CIA(FOIAAdapter):
 
116
  agency = "FBI"
117
  search_url = "https://vault.fbi.gov/search?SearchableText={q}"
118
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
  ALL_ADAPTERS = {
120
  "CIA": CIA(),
121
  "FBI": FBI(),
 
 
 
 
122
  }
123
 
124
  # ======================================================
125
+ # TRUE PDF THUMBNAILS
126
+ # ======================================================
127
+
128
+ def generate_pdf_thumbnails(url, max_pages=2):
129
+ if not PDF_THUMBNAILS_AVAILABLE:
130
+ return []
131
+ try:
132
+ r = requests.get(url, timeout=10)
133
+ images = convert_from_bytes(r.content, first_page=1, last_page=max_pages)
134
+ thumbs = []
135
+ for img in images:
136
+ buf = io.BytesIO()
137
+ img.save(buf, format="PNG")
138
+ thumbs.append(base64.b64encode(buf.getvalue()).decode())
139
+ return thumbs
140
+ except Exception:
141
+ return []
142
+
143
+ # ======================================================
144
+ # REAL PDF TEXT EXTRACTION (PUBLIC DOCS ONLY)
145
  # ======================================================
146
 
147
+ def extract_pdf_text(url, limit=1500):
148
+ if not PDF_TEXT_EXTRACTION_AVAILABLE:
149
+ return "PDF text extraction not available in this environment."
150
  try:
151
+ r = requests.get(url, timeout=10)
152
+ reader = PdfReader(io.BytesIO(r.content))
153
+ text = ""
154
+ for page in reader.pages[:5]:
155
+ text += page.extract_text() or ""
156
+ return text[:limit]
157
  except Exception:
158
+ return "Unable to extract text from PDF."
159
 
160
  # ======================================================
161
  # SEARCH
162
  # ======================================================
163
 
164
  def run_search(query, agencies):
165
+ global LAST_RESULTS
 
166
  LAST_RESULTS = []
 
167
 
168
+ rows = []
169
  for name in agencies:
170
+ for r in ALL_ADAPTERS[name].search(query):
171
+ r["hash"] = sha256_text(r["resolved_url"])[:16]
172
+ r["thumbnails"] = (
173
+ generate_pdf_thumbnails(r["resolved_url"])
174
+ if r["resolved_url"].lower().endswith(".pdf")
175
+ else []
176
+ )
177
+ r["extracted_text"] = (
178
+ extract_pdf_text(r["resolved_url"])
179
+ if r["resolved_url"].lower().endswith(".pdf")
180
+ else ""
181
+ )
182
  LAST_RESULTS.append(r)
183
+ rows.append([r["agency"], r["title"], r["resolved_url"], r["hash"]])
 
 
 
 
 
 
184
 
185
+ return rows, render_cards(), "Search complete."
186
 
187
  # ======================================================
188
+ # ASK AI (ASSISTIVE ONLY)
189
  # ======================================================
190
 
 
 
 
 
 
 
 
191
  def ask_ai(index: int):
192
+ global AI_APPENDIX
193
  r = LAST_RESULTS[index]
 
194
 
195
+ text = (
196
+ "AI Assistive Summary (Non-Authoritative)\n\n"
197
  f"Agency: {r['agency']}\n"
198
+ f"Source URL: {r['resolved_url']}\n\n"
199
+ f"Extracted Text Preview:\n{r.get('extracted_text','')[:800]}"
 
 
200
  )
201
 
202
  AI_APPENDIX = {
203
+ "text": text,
204
+ "hash": sha256_text(text),
205
+ "prov": provenance_block(text, ai=True)
206
  }
207
 
208
+ return text + "\n\n" + AI_APPENDIX["prov"]
209
 
210
  # ======================================================
211
+ # CLERK TRAINING – ACTUAL PDF SLIDES
212
  # ======================================================
213
 
214
+ def generate_clerk_training_pdf():
215
+ path = os.path.join(tempfile.gettempdir(), "Judicial_Clerk_Training.pdf")
216
+ styles = getSampleStyleSheet()
217
+ doc = SimpleDocTemplate(path, pagesize=LETTER)
218
+ story = []
219
+
220
+ slides = [
221
+ "FOIA Intelligence Tool – Clerk Training",
222
+ "What This Tool Is\n\n• Public FOIA link-out search\n• No scraping\n• No sealed data",
223
+ "What This Tool Is NOT\n\n• Not evidence\n• Not legal advice\n• Not authentication",
224
+ "AI Usage\n\n• User-initiated\n• Assistive only\n• Cryptographically hashed",
225
+ "CM/ECF Compatibility\n\n• Informational exhibits\n• Hash-verifiable\n• No metadata mutation",
226
+ ]
227
+
228
+ for s in slides:
229
+ story.append(Paragraph(s.replace("\n", "<br/>"), styles["Title"]))
230
+ story.append(PageBreak())
231
+
232
+ doc.build(story)
233
+ return path
234
+
235
+ # ======================================================
236
+ # AO / CM-ECF COMPATIBILITY MEMO
237
+ # ======================================================
238
+
239
+ AO_CMECF_MEMO = """
240
+ Administrative Office / CM-ECF Compatibility Memo
241
+
242
+ • This system produces informational exhibits only
243
+ • No filing automation or docket access
244
+ • No PACER integration
245
+ • Hashes provided for verification
246
+ • AI output segregated and labeled
247
+
248
+ Compatible with AO guidance on non-filing research tools.
249
+ """
250
 
251
  # ======================================================
252
+ # COURT BUNDLE
253
  # ======================================================
254
 
255
  def generate_court_bundle():
 
256
  with tempfile.TemporaryDirectory() as td:
257
+ path = os.path.join(td, "court_bundle.zip")
258
+ with zipfile.ZipFile(path, "w") as z:
259
  for i, r in enumerate(LAST_RESULTS, 1):
 
 
 
 
 
 
 
 
 
 
 
260
  z.writestr(
261
+ f"Exhibit_{i:03d}.txt",
262
+ f"{r['resolved_url']}\n\n{provenance_block(r['resolved_url'])}"
263
  )
 
264
 
265
+ if AI_APPENDIX:
266
+ z.writestr("AI_Appendix.txt", AI_APPENDIX["text"])
267
+ z.writestr("AI_Appendix.provenance.txt", AI_APPENDIX["prov"])
 
 
268
 
269
+ z.writestr("AO_CMECF_Memo.txt", AO_CMECF_MEMO)
270
+ z.writestr("FAISS_Phase4_Workflow.txt", FAISS_APPROVAL_MEMO)
 
271
 
272
+ return path
 
 
 
 
 
273
 
274
  # ======================================================
275
  # UI
276
  # ======================================================
277
 
278
+ def render_cards():
279
+ cards = []
280
+ for i, r in enumerate(LAST_RESULTS):
281
+ thumbs = "".join(
282
+ f'<img src="data:image/png;base64,{t}" style="width:120px;margin-right:8px;border-radius:6px;" />'
283
+ for t in r["thumbnails"]
284
+ )
285
+ cards.append(f"""
286
+ <div style="border:1px solid #444;border-radius:14px;padding:14px;margin-bottom:14px;">
287
+ <b>{r['agency']}</b><br/>
288
+ {r['title']}<br/>
289
+ {thumbs}
290
+ <div style="margin-top:8px;">
291
+ <a href="{r['resolved_url']}" target="_blank">View</a>
292
+ &nbsp;|&nbsp;
293
+ <button onclick="fetch('/ask_ai?index={i}')"
294
+ style="background:#1e88e5;color:white;border:none;border-radius:999px;padding:4px 12px;">
295
+ Ask AI
296
+ </button>
297
+ </div>
298
+ </div>
299
+ """)
300
+ return "".join(cards)
301
 
302
+ with gr.Blocks() as demo:
303
+ gr.Markdown("## Federal FOIA Intelligence Search")
 
 
 
304
 
305
  with gr.Tab("Search"):
306
+ agencies = gr.CheckboxGroup(list(ALL_ADAPTERS.keys()), value=list(ALL_ADAPTERS.keys()))
307
+ query = gr.Textbox(placeholder="Search FOIA reading rooms")
308
+ table = gr.Dataframe(headers=["Agency", "Title", "URL", "Hash"])
309
+ cards = gr.HTML()
310
+ status = gr.Textbox()
311
+ gr.Button("Search").click(run_search, [query, agencies], [table, cards, status])
312
+
313
+ with gr.Tab("Court / Clerk"):
314
+ gr.Button("Download Clerk Training PDF").click(
315
+ lambda: generate_clerk_training_pdf(),
316
+ None,
317
+ gr.File()
 
318
  )
 
 
319
  gr.Button("Generate Court Bundle").click(
320
  lambda: generate_court_bundle(),
321
  None,
322
+ gr.File()
323
  )
324
 
325
+ with gr.Tab("Trust & Governance"):
326
+ gr.Markdown(AO_CMECF_MEMO)
327
+ gr.Markdown(FAISS_APPROVAL_MEMO)
 
 
 
 
 
 
328
 
329
+ demo.queue()
330
+ demo.launch(server_name="0.0.0.0", server_port=7860)