Rajan Sharma commited on
Commit
c0550c0
·
verified ·
1 Parent(s): 10cd369

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +91 -92
app.py CHANGED
@@ -10,17 +10,17 @@ from audit_log import log_event, hash_summary
10
  from privacy import redact_text
11
 
12
  # ---------- Environment / cache (Spaces-safe, writable) ----------
13
- HOME = pathlib.Path.home() # /home/user on Spaces
14
  HF_HOME = str(HOME / ".cache" / "huggingface")
15
  HF_HUB_CACHE = str(HOME / ".cache" / "huggingface" / "hub")
16
  HF_TRANSFORMERS = str(HOME / ".cache" / "huggingface" / "transformers")
17
  ST_HOME = str(HOME / ".cache" / "sentence-transformers")
18
- GRADIO_TMP = str(HOME / "app" / "gradio") # or "/tmp/gradio"
19
  GRADIO_CACHE = GRADIO_TMP
20
 
21
  os.environ.setdefault("HF_HOME", HF_HOME)
22
  os.environ.setdefault("HF_HUB_CACHE", HF_HUB_CACHE)
23
- os.environ.setdefault("TRANSFORMERS_CACHE", HF_TRANSFORMERS) # warning is fine
24
  os.environ.setdefault("SENTENCE_TRANSFORMERS_HOME", ST_HOME)
25
  os.environ.setdefault("GRADIO_TEMP_DIR", GRADIO_TMP)
26
  os.environ.setdefault("GRADIO_CACHE_DIR", GRADIO_CACHE)
@@ -52,13 +52,13 @@ from session_rag import SessionRAG
52
  from mdsi_analysis import capacity_projection, cost_estimate, outcomes_summary
53
 
54
  # ---------- Config ----------
55
- MODEL_ID = os.getenv("MODEL_ID", "microsoft/Phi-3-mini-4k-instruct") # local fallback
56
  HF_TOKEN = os.getenv("HUGGINGFACE_HUB_TOKEN") or os.getenv("HF_TOKEN")
57
 
58
  COHERE_API_KEY = os.getenv("COHERE_API_KEY")
59
  USE_HOSTED_COHERE = bool(COHERE_API_KEY and _HAS_COHERE)
60
 
61
- # BIGGER OUTPUT LIMIT
62
  MAX_NEW_TOKENS = int(os.getenv("MAX_NEW_TOKENS", "2048"))
63
 
64
  # ---------- System Master (two-phase, LLM-only behavior) ----------
@@ -152,7 +152,7 @@ def cohere_chat(message, history):
152
  model="command-r7b-12-2024",
153
  message=prompt,
154
  temperature=0.3,
155
- max_tokens=MAX_NEW_TOKENS, # uses 2048
156
  )
157
  if hasattr(resp, "text") and resp.text: return resp.text.strip()
158
  if hasattr(resp, "reply") and resp.reply: return resp.reply.strip()
@@ -204,7 +204,7 @@ def local_generate(model, tokenizer, input_ids, max_new_tokens=MAX_NEW_TOKENS):
204
  input_ids = input_ids.to(model.device)
205
  with torch.no_grad():
206
  out = model.generate(
207
- input_ids=input_ids, max_new_tokens=max_new_tokens, # uses 2048
208
  do_sample=True, temperature=0.3, top_p=0.9,
209
  repetition_penalty=1.15,
210
  pad_token_id=tokenizer.eos_token_id,
@@ -264,7 +264,7 @@ def clarityops_reply(user_msg, history, tz, uploaded_files_paths, awaiting_answe
264
  ans = "I am ClarityOps, your strategic decision making AI partner."
265
  return history + [(user_msg, ans)], awaiting_answers
266
 
267
- # Ingest uploads (text + artifacts like CSV headers)
268
  if uploaded_files_paths:
269
  ing = extract_text_from_files(uploaded_files_paths)
270
  chunks = ing.get("chunks", []) if isinstance(ing, dict) else (ing or [])
@@ -370,131 +370,130 @@ theme = gr.themes.Soft(primary_hue="teal", neutral_hue="slate", radius_size=gr.t
370
  custom_css = """
371
  :root { --brand-bg: #e6f7f8; --brand-accent: #0d9488; --brand-text: #0f172a; --brand-text-light: #ffffff; }
372
 
373
- /* Full-height layout */
374
  html, body, .gradio-container { height: 100vh; }
375
  .gradio-container { background: var(--brand-bg); display: flex; flex-direction: column; }
376
 
377
- /* Tidy typography */
378
- h1 { color: var(--brand-text); font-weight: 700; font-size: 28px !important; }
 
 
 
 
 
379
 
380
- /* Minimal chrome */
381
- .chatbot header, .chatbot .label, .chatbot .label-wrap, .chatbot .top, .chatbot .header, .chatbot > .wrap > header { display: none !important; }
382
  .message.user, .message.bot { background: var(--brand-accent) !important; color: var(--brand-text-light) !important; border-radius: 12px !important; padding: 8px 12px !important; }
 
383
  textarea, input, .gr-input { border-radius: 12px !important; }
384
-
385
- /* Anchor for overlay */
386
- #chat-container { position: relative; }
387
-
388
- /* Centered handshake overlay INSIDE the chat area */
389
- #handshake-overlay {
390
- position: absolute;
391
- top: 0; left: 0; right: 0; bottom: 0; /* fully cover the chat surface */
392
- margin: 8px; /* reveal rounded corners of chat */
393
- z-index: 9999; /* sit above the chat content */
394
- display: grid;
395
- place-items: center;
396
- pointer-events: none; /* do not block input below */
397
- }
398
-
399
- #handshake-overlay .panel {
400
- pointer-events: auto; /* if you later want to click to dismiss */
401
- background: rgba(13, 148, 136, 0.96);
402
- color: #fff;
403
- padding: 18px 22px;
404
- border-radius: 14px;
405
- font-size: 16px;
406
- max-width: 720px;
407
- text-align: center;
408
- box-shadow: 0 10px 24px rgba(0,0,0,0.2);
409
- }
410
-
411
- #handshake-overlay.hidden { display: none; }
412
-
413
- @media (max-height: 700px) {
414
- #handshake-overlay .panel { font-size: 14px; padding: 14px 16px; max-width: 90vw; }
415
- }
416
  """
417
 
418
  # ---------- UI ----------
419
  with gr.Blocks(theme=theme, css=custom_css, analytics_enabled=False) as demo:
420
- gr.Markdown("# ClarityOps Augmented Decision AI")
421
-
422
- with gr.Column(elem_id="chat-container"):
423
- chat = gr.Chatbot(label="", show_label=False, height="62vh")
424
- handshake = gr.HTML(
425
- value=(
426
- '<div id="handshake-overlay">'
427
- '<div class="panel">ClarityOps loaded. Paste your scenario and attach files. '
428
- 'I’ll ask up to 5 clarifications, then produce the structured analysis</div>'
429
- '</div>'
 
 
 
 
 
 
 
 
 
 
 
430
  )
431
- )
432
-
433
- with gr.Row():
434
- uploads = gr.Files(
435
- label="Upload docs/images (PDF, DOCX, CSV, PNG, JPG)",
436
- file_types=["file"], file_count="multiple", height=68
437
- )
438
-
439
- with gr.Row():
440
- msg = gr.Textbox(
441
- label="",
442
- show_label=False,
443
- placeholder="Paste your scenario here (attach files below). ClarityOps will ask clarifications first.",
444
- scale=10
445
- )
446
- send = gr.Button("Send", scale=1)
447
- clear = gr.Button("Clear chat", scale=1)
448
 
 
449
  state_history = gr.State(value=[])
450
  state_uploaded = gr.State(value=[])
451
  state_awaiting = gr.State(value=False) # False -> Phase 1 next; True -> awaiting answers for Phase 2
452
 
453
- def _store_uploads_and_hide(files, current):
 
454
  paths = []
455
  for f in (files or []):
456
  paths.append(getattr(f, "name", None) or f)
457
- overlay_hidden = gr.update(value='<div id="handshake-overlay" class="hidden"></div>')
458
- return (current or []) + paths, overlay_hidden
459
 
460
- uploads.change(fn=_store_uploads_and_hide,
461
- inputs=[uploads, state_uploaded],
462
- outputs=[state_uploaded, handshake])
463
 
 
464
  def _on_send(user_msg, history, up_paths, awaiting):
465
- hide_overlay = gr.update(value='<div id="handshake-overlay" class="hidden"></div>')
466
  try:
467
  if not user_msg or not user_msg.strip():
468
- return history, "", history, awaiting, hide_overlay
 
469
  new_history, new_awaiting = clarityops_reply(
470
  user_msg.strip(), history or [], None, up_paths or [], awaiting_answers=awaiting
471
  )
472
- return new_history, "", new_history, new_awaiting, hide_overlay
473
  except Exception as e:
474
  err = f"Error: {e}"
475
  try: traceback.print_exc()
476
  except Exception: pass
477
  new_hist = (history or []) + [(user_msg or "", err)]
478
- return new_hist, "", new_hist, awaiting, hide_overlay
 
 
 
 
 
 
 
 
 
 
479
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
480
  send.click(_on_send, inputs=[msg, state_history, state_uploaded, state_awaiting],
481
- outputs=[chat, msg, state_history, state_awaiting, handshake],
482
  concurrency_limit=2, queue=True)
483
-
484
  msg.submit(_on_send, inputs=[msg, state_history, state_uploaded, state_awaiting],
485
- outputs=[chat, msg, state_history, state_awaiting, handshake],
486
  concurrency_limit=2, queue=True)
487
 
488
  def _on_clear():
489
- # Reset everything, show handshake again
490
- return [], "", [], False, (
491
- '<div id="handshake-overlay"><div class="panel">'
492
- 'ClarityOps loaded. Paste your scenario and attach files. '
493
- 'I’ll ask up to 5 clarifications, then produce the structured analysis'
494
- '</div></div>'
495
  )
496
 
497
- clear.click(_on_clear, None, [chat, msg, state_history, state_awaiting, handshake])
498
 
499
  if __name__ == "__main__":
500
  port = int(os.environ.get("PORT", "7860"))
 
10
  from privacy import redact_text
11
 
12
  # ---------- Environment / cache (Spaces-safe, writable) ----------
13
+ HOME = pathlib.Path.home()
14
  HF_HOME = str(HOME / ".cache" / "huggingface")
15
  HF_HUB_CACHE = str(HOME / ".cache" / "huggingface" / "hub")
16
  HF_TRANSFORMERS = str(HOME / ".cache" / "huggingface" / "transformers")
17
  ST_HOME = str(HOME / ".cache" / "sentence-transformers")
18
+ GRADIO_TMP = str(HOME / "app" / "gradio")
19
  GRADIO_CACHE = GRADIO_TMP
20
 
21
  os.environ.setdefault("HF_HOME", HF_HOME)
22
  os.environ.setdefault("HF_HUB_CACHE", HF_HUB_CACHE)
23
+ os.environ.setdefault("TRANSFORMERS_CACHE", HF_TRANSFORMERS)
24
  os.environ.setdefault("SENTENCE_TRANSFORMERS_HOME", ST_HOME)
25
  os.environ.setdefault("GRADIO_TEMP_DIR", GRADIO_TMP)
26
  os.environ.setdefault("GRADIO_CACHE_DIR", GRADIO_CACHE)
 
52
  from mdsi_analysis import capacity_projection, cost_estimate, outcomes_summary
53
 
54
  # ---------- Config ----------
55
+ MODEL_ID = os.getenv("MODEL_ID", "microsoft/Phi-3-mini-4k-instruct") # fallback
56
  HF_TOKEN = os.getenv("HUGGINGFACE_HUB_TOKEN") or os.getenv("HF_TOKEN")
57
 
58
  COHERE_API_KEY = os.getenv("COHERE_API_KEY")
59
  USE_HOSTED_COHERE = bool(COHERE_API_KEY and _HAS_COHERE)
60
 
61
+ # Larger output (Cohere + HF fallback)
62
  MAX_NEW_TOKENS = int(os.getenv("MAX_NEW_TOKENS", "2048"))
63
 
64
  # ---------- System Master (two-phase, LLM-only behavior) ----------
 
152
  model="command-r7b-12-2024",
153
  message=prompt,
154
  temperature=0.3,
155
+ max_tokens=MAX_NEW_TOKENS,
156
  )
157
  if hasattr(resp, "text") and resp.text: return resp.text.strip()
158
  if hasattr(resp, "reply") and resp.reply: return resp.reply.strip()
 
204
  input_ids = input_ids.to(model.device)
205
  with torch.no_grad():
206
  out = model.generate(
207
+ input_ids=input_ids, max_new_tokens=max_new_tokens,
208
  do_sample=True, temperature=0.3, top_p=0.9,
209
  repetition_penalty=1.15,
210
  pad_token_id=tokenizer.eos_token_id,
 
264
  ans = "I am ClarityOps, your strategic decision making AI partner."
265
  return history + [(user_msg, ans)], awaiting_answers
266
 
267
+ # Ingest uploads
268
  if uploaded_files_paths:
269
  ing = extract_text_from_files(uploaded_files_paths)
270
  chunks = ing.get("chunks", []) if isinstance(ing, dict) else (ing or [])
 
370
  custom_css = """
371
  :root { --brand-bg: #e6f7f8; --brand-accent: #0d9488; --brand-text: #0f172a; --brand-text-light: #ffffff; }
372
 
 
373
  html, body, .gradio-container { height: 100vh; }
374
  .gradio-container { background: var(--brand-bg); display: flex; flex-direction: column; }
375
 
376
+ /* HERO (landing) */
377
+ #hero-wrap { height: 70vh; display: grid; place-items: center; }
378
+ #hero { text-align: center; }
379
+ #hero h2 { color: #0f172a; font-weight: 800; font-size: 32px; margin-bottom: 22px; }
380
+ #hero .search-row { width: min(860px, 92vw); margin: 0 auto; display: flex; gap: 8px; }
381
+ #hero .search-row .hero-box { flex: 1 1 auto; }
382
+ #hero .hint { color: #334155; margin-top: 10px; font-size: 13px; opacity: 0.9; }
383
 
384
+ /* CHAT */
385
+ #chat-container { position: relative; }
386
  .message.user, .message.bot { background: var(--brand-accent) !important; color: var(--brand-text-light) !important; border-radius: 12px !important; padding: 8px 12px !important; }
387
+ .chatbot header, .chatbot .label, .chatbot .label-wrap { display: none !important; }
388
  textarea, input, .gr-input { border-radius: 12px !important; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
389
  """
390
 
391
  # ---------- UI ----------
392
  with gr.Blocks(theme=theme, css=custom_css, analytics_enabled=False) as demo:
393
+ # --- HERO (initial Google-like screen) ---
394
+ with gr.Column(elem_id="hero-wrap", visible=True) as hero_wrap:
395
+ with gr.Column(elem_id="hero"):
396
+ gr.HTML("<h2>What can I help with?</h2>")
397
+ with gr.Row(elem_classes="search-row"):
398
+ hero_msg = gr.Textbox(
399
+ placeholder="Ask anything (paste scenarios here; you can attach files after)...",
400
+ show_label=False,
401
+ lines=1,
402
+ elem_classes="hero-box"
403
+ )
404
+ hero_send = gr.Button("➤", scale=0)
405
+ gr.Markdown('<div class="hint">ClarityOps will first ask up to 5 clarifications, then produce a structured analysis.</div>')
406
+
407
+ # --- MAIN APP (hidden until first message) ---
408
+ with gr.Column(elem_id="chat-container", visible=False) as app_wrap:
409
+ chat = gr.Chatbot(label="", show_label=False, height="64vh")
410
+ with gr.Row():
411
+ uploads = gr.Files(
412
+ label="Upload docs/images (PDF, DOCX, CSV, PNG, JPG)",
413
+ file_types=["file"], file_count="multiple", height=68
414
  )
415
+ with gr.Row():
416
+ msg = gr.Textbox(
417
+ label="",
418
+ show_label=False,
419
+ placeholder="Continue here. Paste scenario details, add files below.",
420
+ scale=10
421
+ )
422
+ send = gr.Button("Send", scale=1)
423
+ clear = gr.Button("Clear chat", scale=1)
 
 
 
 
 
 
 
 
424
 
425
+ # ---- State
426
  state_history = gr.State(value=[])
427
  state_uploaded = gr.State(value=[])
428
  state_awaiting = gr.State(value=False) # False -> Phase 1 next; True -> awaiting answers for Phase 2
429
 
430
+ # ---- Uploads
431
+ def _store_uploads(files, current):
432
  paths = []
433
  for f in (files or []):
434
  paths.append(getattr(f, "name", None) or f)
435
+ return (current or []) + paths
 
436
 
437
+ uploads.change(fn=_store_uploads, inputs=[uploads, state_uploaded], outputs=state_uploaded)
 
 
438
 
439
+ # ---- Core send (used by both hero input and chat input)
440
  def _on_send(user_msg, history, up_paths, awaiting):
 
441
  try:
442
  if not user_msg or not user_msg.strip():
443
+ # no toggle on empty
444
+ return history, "", history, awaiting
445
  new_history, new_awaiting = clarityops_reply(
446
  user_msg.strip(), history or [], None, up_paths or [], awaiting_answers=awaiting
447
  )
448
+ return new_history, "", new_history, new_awaiting
449
  except Exception as e:
450
  err = f"Error: {e}"
451
  try: traceback.print_exc()
452
  except Exception: pass
453
  new_hist = (history or []) + [(user_msg or "", err)]
454
+ return new_hist, "", new_hist, awaiting
455
+
456
+ # ---- Hero -> App transition + first send
457
+ def _hero_start(user_msg, history, up_paths, awaiting):
458
+ chat_o, msg_o, hist_o, await_o = _on_send(user_msg, history, up_paths, awaiting)
459
+ return (
460
+ chat_o, msg_o, hist_o, await_o,
461
+ gr.update(visible=False), # hide hero
462
+ gr.update(visible=True), # show app
463
+ "" # clear hero box
464
+ )
465
 
466
+ hero_send.click(
467
+ _hero_start,
468
+ inputs=[hero_msg, state_history, state_uploaded, state_awaiting],
469
+ outputs=[chat, msg, state_history, state_awaiting, hero_wrap, app_wrap, hero_msg],
470
+ concurrency_limit=2, queue=True
471
+ )
472
+ hero_msg.submit(
473
+ _hero_start,
474
+ inputs=[hero_msg, state_history, state_uploaded, state_awaiting],
475
+ outputs=[chat, msg, state_history, state_awaiting, hero_wrap, app_wrap, hero_msg],
476
+ concurrency_limit=2, queue=True
477
+ )
478
+
479
+ # ---- Normal chat interactions after hero is gone
480
  send.click(_on_send, inputs=[msg, state_history, state_uploaded, state_awaiting],
481
+ outputs=[chat, msg, state_history, state_awaiting],
482
  concurrency_limit=2, queue=True)
 
483
  msg.submit(_on_send, inputs=[msg, state_history, state_uploaded, state_awaiting],
484
+ outputs=[chat, msg, state_history, state_awaiting],
485
  concurrency_limit=2, queue=True)
486
 
487
  def _on_clear():
488
+ # reset to fresh hero screen
489
+ return (
490
+ [], "", [], False,
491
+ gr.update(visible=True), # show hero
492
+ gr.update(visible=False), # hide app
493
+ "" # clear hero input
494
  )
495
 
496
+ clear.click(_on_clear, None, [chat, msg, state_history, state_awaiting, hero_wrap, app_wrap, hero_msg])
497
 
498
  if __name__ == "__main__":
499
  port = int(os.environ.get("PORT", "7860"))