mmalam786 commited on
Commit
de6d6dd
Β·
verified Β·
1 Parent(s): 0e13152

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +127 -3
app.py CHANGED
@@ -256,6 +256,92 @@ def completions(request: Request, body: ChatRequest):
256
  "iom_receipt_id": receipt["receipt_id"]
257
  }
258
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
259
  # ========= UI helpers =========
260
  def _load_events_df():
261
  if not os.path.exists(EVENTS_PATH):
@@ -351,7 +437,7 @@ with api_ui:
351
  gr.Markdown("# IoM-TR Receipts β€” Portable Governance (NIM + HF Space)")
352
 
353
  with gr.Tab("Ask & Verify (in this Space)"):
354
- gr.Markdown("Type a prompt, click **Ask & Get Receipt**. You’ll see the answer, a Receipt ID, the full receipt JSON, and a built-in verification result (SHA/HMAC).")
355
  p2 = gr.Textbox(label="Prompt", value="In one sentence, what are portable AI governance receipts?")
356
  ask = gr.Button("Ask & Get Receipt")
357
  ans = gr.Textbox(label="Answer")
@@ -377,7 +463,45 @@ with api_ui:
377
  verdict2 = gr.Textbox(label="Result")
378
  check.click(verify_local, inputs=[p, a, rj, pepper], outputs=verdict2)
379
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
380
  # Expose the ASGI app for Spaces (Docker or Gradio SDK)
381
- # Optional: exposing demo can help the Gradio runner, but not required for Docker
382
  demo = api_ui
383
- app = gr.mount_gradio_app(api, api_ui, path="/")
 
256
  "iom_receipt_id": receipt["receipt_id"]
257
  }
258
 
259
+ # ========= Session & Merkle helpers =========
260
+ SESSIONS_DIR = pathlib.Path("/tmp/sessions"); SESSIONS_DIR.mkdir(exist_ok=True)
261
+
262
+ def _session_path(sid: str) -> pathlib.Path:
263
+ return SESSIONS_DIR / f"{sid}.json"
264
+
265
+ def merkle_root_from_leaves(leaves: list[str]) -> str:
266
+ """Compute a simple Merkle root from hex-string leaves.
267
+ Parent = sha256_hex(left + right) (deterministic demo)."""
268
+ if not leaves:
269
+ return ""
270
+ level = leaves[:]
271
+ while len(level) > 1:
272
+ if len(level) % 2 == 1:
273
+ level.append(level[-1]) # duplicate last if odd
274
+ nxt = []
275
+ for i in range(0, len(level), 2):
276
+ nxt.append(sha256_hex(level[i] + level[i+1]))
277
+ level = nxt
278
+ return level[0]
279
+
280
+ def leaf_from_receipt(r: dict) -> str:
281
+ """Leaf = sha256( prompt_sha256 + response_sha256 )."""
282
+ p = r.get("prompt_sha256", "")
283
+ a = r.get("response_sha256", "")
284
+ return sha256_hex(p + a)
285
+
286
+ def session_new() -> dict:
287
+ sid = str(uuid.uuid4())
288
+ doc = {"id": sid, "created": datetime.utcnow().isoformat()+"Z",
289
+ "receipts": [], "leaf_hashes": [], "policy_ids": [], "merkle_root": ""}
290
+ _session_path(sid).write_text(json.dumps(doc, indent=2))
291
+ return doc
292
+
293
+ def session_load(sid: str) -> dict:
294
+ p = _session_path(sid)
295
+ if not p.exists():
296
+ raise FileNotFoundError("session not found")
297
+ return json.loads(p.read_text())
298
+
299
+ def session_save(doc: dict):
300
+ _session_path(doc["id"]).write_text(json.dumps(doc, indent=2))
301
+
302
+ def session_add_receipt(sid: str, rid: str) -> dict:
303
+ doc = session_load(sid)
304
+ rp = RECEIPTS_DIR / f"{rid}.json"
305
+ if not rp.exists():
306
+ raise FileNotFoundError("receipt not found")
307
+ rj = json.loads(rp.read_text())
308
+ leaf = leaf_from_receipt(rj)
309
+ doc["receipts"].append(rid)
310
+ doc["leaf_hashes"].append(leaf)
311
+ pid = rj.get("policy_id")
312
+ if pid and pid not in doc["policy_ids"]:
313
+ doc["policy_ids"].append(pid)
314
+ doc["merkle_root"] = "" # will be set on finish
315
+ session_save(doc)
316
+ return doc
317
+
318
+ def session_finish(sid: str) -> dict:
319
+ doc = session_load(sid)
320
+ doc["merkle_root"] = merkle_root_from_leaves(doc["leaf_hashes"])
321
+ doc["finished"] = datetime.utcnow().isoformat()+"Z"
322
+ session_save(doc)
323
+ return doc
324
+
325
+ # ========= Session API =========
326
+ @api.post("/session/new")
327
+ def api_session_new():
328
+ doc = session_new()
329
+ return {"session_id": doc["id"]}
330
+
331
+ @api.post("/session/{sid}/add/{rid}")
332
+ def api_session_add(sid: str, rid: str):
333
+ doc = session_add_receipt(sid, rid)
334
+ return {"session_id": doc["id"], "count": len(doc["receipts"])}
335
+
336
+ @api.post("/session/{sid}/finish")
337
+ def api_session_finish(sid: str):
338
+ doc = session_finish(sid)
339
+ return {"session_id": doc["id"], "merkle_root": doc["merkle_root"]}
340
+
341
+ @api.get("/session/{sid}")
342
+ def api_session_get(sid: str):
343
+ return session_load(sid)
344
+
345
  # ========= UI helpers =========
346
  def _load_events_df():
347
  if not os.path.exists(EVENTS_PATH):
 
437
  gr.Markdown("# IoM-TR Receipts β€” Portable Governance (NIM + HF Space)")
438
 
439
  with gr.Tab("Ask & Verify (in this Space)"):
440
+ gr.Markdown("Type a prompt, click **Ask & Get Receipt**. The UI shows the answer, a Receipt ID, the full receipt JSON, and local verification (SHA/HMAC).")
441
  p2 = gr.Textbox(label="Prompt", value="In one sentence, what are portable AI governance receipts?")
442
  ask = gr.Button("Ask & Get Receipt")
443
  ans = gr.Textbox(label="Answer")
 
463
  verdict2 = gr.Textbox(label="Result")
464
  check.click(verify_local, inputs=[p, a, rj, pepper], outputs=verdict2)
465
 
466
+ with gr.Tab("Session Root"):
467
+ gr.Markdown("**Create a session β†’ add last receipt(s) β†’ finish β†’ get one Merkle root + proof JSON.**")
468
+
469
+ sid_tb = gr.Textbox(label="Session ID", interactive=False)
470
+ sess_status = gr.Textbox(label="Status", interactive=False)
471
+ count_tb = gr.Number(label="Receipts in session", value=0, interactive=False)
472
+ root_tb = gr.Textbox(label="Merkle root", interactive=False)
473
+ proof_code = gr.Code(label="Session proof JSON (snapshot)", language="json")
474
+ proof_link = gr.Markdown()
475
+
476
+ def ui_session_new():
477
+ d = session_new()
478
+ return d["id"], "Session started", 0, "", "", f"[Open proof](/session/{d['id']})"
479
+
480
+ def ui_session_add_last(sid: str, last_rid: str):
481
+ if not sid:
482
+ return "", "Start a session first", 0, "", "", ""
483
+ if not last_rid:
484
+ return sid, "Run 'Ask & Get Receipt' first (to produce a receipt)", 0, "", "", f"[Open proof](/session/{sid})"
485
+ d = session_add_receipt(sid, last_rid)
486
+ snap = json.dumps(d, indent=2)
487
+ return d["id"], f"Added receipt {last_rid}", len(d["receipts"]), d.get("merkle_root",""), snap, f"[Open proof](/session/{d['id']})"
488
+
489
+ def ui_session_finish(sid: str):
490
+ if not sid:
491
+ return "", "Start a session first", 0, "", "", ""
492
+ d = session_finish(sid)
493
+ snap = json.dumps(d, indent=2)
494
+ return d["id"], "Session finished", len(d["receipts"]), d["merkle_root"], snap, f"[Open proof](/session/{d['id']})"
495
+
496
+ start_btn = gr.Button("Start new session")
497
+ add_btn = gr.Button("Add last receipt from 'Ask & Verify'")
498
+ finish_btn= gr.Button("Finish session (compute root)")
499
+
500
+ start_btn.click(ui_session_new, outputs=[sid_tb, sess_status, count_tb, root_tb, proof_code, proof_link])
501
+ # reuse the 'rid' Textbox from the Ask & Verify tab as input here
502
+ add_btn.click(ui_session_add_last, inputs=[sid_tb, rid], outputs=[sid_tb, sess_status, count_tb, root_tb, proof_code, proof_link])
503
+ finish_btn.click(ui_session_finish, inputs=[sid_tb], outputs=[sid_tb, sess_status, count_tb, root_tb, proof_code, proof_link])
504
+
505
  # Expose the ASGI app for Spaces (Docker or Gradio SDK)
 
506
  demo = api_ui
507
+ app = gr.mount_gradio_app(api, api_ui, path="/")