File size: 4,906 Bytes
b14fc84 661eb14 01f6914 661eb14 b14fc84 661eb14 1049372 01f6914 b2ad034 b14fc84 1049372 b14fc84 1049372 661eb14 1049372 01f6914 1049372 01f6914 661eb14 b2ad034 1049372 b2ad034 1049372 b2ad034 661eb14 01f6914 1049372 661eb14 01f6914 b14fc84 01f6914 b14fc84 01f6914 b14fc84 01f6914 1049372 01f6914 1049372 b14fc84 1049372 661eb14 1049372 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | import shutil
import streamlit as st
from ui.styles import CSS
from ui.tab_overview import render as render_overview
from ui.tab_tender import render as render_tender
from ui.tab_bidders import render as render_bidders
from ui.tab_review import render as render_review
from ui.tab_audit import render as render_audit
from ui.tab_interpretability import render as render_interpretability
st.set_page_config(page_title="TenderIQ", page_icon="⚖️", layout="wide")
st.markdown(CSS, unsafe_allow_html=True)
def _probe_llm() -> str:
if st.session_state.get("fallback_active"):
return "amber"
if "llm_status" in st.session_state:
return st.session_state["llm_status"]
from core.llm_client import LLM, LLMUnavailable
try:
LLM().chat_json("Respond with valid JSON only.", '{"ping": true}')
st.session_state["llm_status"] = "green"
return "green"
except Exception:
st.session_state["llm_status"] = "red"
return "red"
def _reset_demo() -> None:
from core import audit
from core.config import CHROMA_DIR, OCR_CACHE_DIR
audit.clear()
shutil.rmtree(CHROMA_DIR, ignore_errors=True)
shutil.rmtree(str(OCR_CACHE_DIR), ignore_errors=True)
st.cache_resource.clear()
for k in list(st.session_state.keys()):
del st.session_state[k]
# ── Sidebar ───────────────────────────────────────────────────────────────────
with st.sidebar:
# Branding
st.markdown(
"""<div style="padding:16px 8px 12px;text-align:center;">
<div style="font-size:2.6rem;">⚖️</div>
<div style="font-size:1.4rem;font-weight:800;color:#F8FAFC;
letter-spacing:-0.02em;margin-top:4px;
font-family:Inter,sans-serif;">TenderIQ</div>
<div style="font-size:0.7rem;color:#64748B;margin-top:4px;
text-transform:uppercase;letter-spacing:0.1em;">
AI Tender Evaluation</div>
</div>""",
unsafe_allow_html=True,
)
st.divider()
# Connection status
status = _probe_llm()
dot_color = {"green": "#22C55E", "amber": "#F59E0B", "red": "#EF4444"}[status]
dot_shadow = {"green": "#22C55E", "amber": "#F59E0B", "red": "#EF4444"}[status]
status_label = {"green": "DeepSeek Connected", "amber": "Pre-computed Mode",
"red": "No API Key"}[status]
st.markdown(
f"""<div style="display:flex;align-items:center;gap:9px;
padding:8px 4px;margin-bottom:4px;">
<div style="width:9px;height:9px;border-radius:50%;flex-shrink:0;
background:{dot_color};
box-shadow:0 0 0 3px {dot_color}33,0 0 8px {dot_shadow}88;">
</div>
<span style="font-size:0.82rem;font-weight:600;color:#E2E8F0;">
{status_label}</span>
</div>""",
unsafe_allow_html=True,
)
if status == "amber":
st.warning("Using pre-computed results.")
elif status == "red":
st.caption("Set DEEPSEEK_API_KEY in .env to enable live mode.")
st.divider()
if st.button("↺ Reset Session", use_container_width=True):
for k in list(st.session_state.keys()):
del st.session_state[k]
st.rerun()
if st.button("🗑 Reset for Demo", use_container_width=True, type="secondary"):
st.session_state["confirm_demo_reset"] = True
if st.session_state.get("confirm_demo_reset"):
st.warning("Clears audit log, vector index, OCR cache, and session.")
c1, c2 = st.columns(2)
if c1.button("Yes, reset", type="primary", use_container_width=True):
_reset_demo()
st.rerun()
if c2.button("Cancel", use_container_width=True):
st.session_state.pop("confirm_demo_reset", None)
st.rerun()
st.divider()
st.markdown(
"""<div style="font-size:0.7rem;color:#334155;text-align:center;
line-height:1.6;padding:0 4px;">
CRPF Hackathon · Theme 3<br>
Explainable AI for Government Procurement
</div>""",
unsafe_allow_html=True,
)
# ── Tabs ──────────────────────────────────────────────────────────────────────
tab1, tab2, tab3, tab4, tab5, tab6 = st.tabs([
"🏠 Overview",
"📄 Tender Analysis",
"⚖️ Bidder Evaluation",
"👤 Human Review",
"📋 Audit Log",
"🔍 Interpretability",
])
with tab1: render_overview()
with tab2: render_tender()
with tab3: render_bidders()
with tab4: render_review()
with tab5: render_audit()
with tab6: render_interpretability()
|