Spaces:
Sleeping
Sleeping
Rajan Sharma
commited on
Update app.py
Browse files
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()
|
| 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,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") #
|
| 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 |
-
#
|
| 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,
|
| 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,
|
| 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
|
| 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 |
-
/*
|
| 378 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 379 |
|
| 380 |
-
/*
|
| 381 |
-
|
| 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 |
-
|
| 421 |
-
|
| 422 |
-
|
| 423 |
-
|
| 424 |
-
|
| 425 |
-
|
| 426 |
-
|
| 427 |
-
|
| 428 |
-
|
| 429 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 430 |
)
|
| 431 |
-
)
|
| 432 |
-
|
| 433 |
-
|
| 434 |
-
|
| 435 |
-
|
| 436 |
-
|
| 437 |
-
|
| 438 |
-
|
| 439 |
-
|
| 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 |
-
|
|
|
|
| 454 |
paths = []
|
| 455 |
for f in (files or []):
|
| 456 |
paths.append(getattr(f, "name", None) or f)
|
| 457 |
-
|
| 458 |
-
return (current or []) + paths, overlay_hidden
|
| 459 |
|
| 460 |
-
uploads.change(fn=
|
| 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 |
-
|
|
|
|
| 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
|
| 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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 479 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 484 |
msg.submit(_on_send, inputs=[msg, state_history, state_uploaded, state_awaiting],
|
| 485 |
-
outputs=[chat, msg, state_history, state_awaiting
|
| 486 |
concurrency_limit=2, queue=True)
|
| 487 |
|
| 488 |
def _on_clear():
|
| 489 |
-
#
|
| 490 |
-
return
|
| 491 |
-
|
| 492 |
-
|
| 493 |
-
|
| 494 |
-
|
| 495 |
)
|
| 496 |
|
| 497 |
-
clear.click(_on_clear, None, [chat, msg, state_history, state_awaiting,
|
| 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"))
|