File size: 5,229 Bytes
661eb14 b2ad034 b14fc84 1049372 b2ad034 661eb14 aff4140 b2ad034 1049372 aff4140 1049372 01f6914 b2ad034 1049372 b384020 1049372 b2ad034 aff4140 b2ad034 1049372 aff4140 1049372 01f6914 1049372 aff4140 1049372 aff4140 1049372 aff4140 1049372 aff4140 1049372 aff4140 1049372 aff4140 1049372 aff4140 1049372 b2ad034 01f6914 1049372 01f6914 b2ad034 01f6914 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 | import streamlit as st
from core import audit
from core.config import BIDDER_NAMES
from ui.components import stat_card
def render() -> None:
# Hero — intentional dark gradient, works as a visual anchor in both modes
st.markdown(
"""<div style="background:linear-gradient(135deg,#0D1B2A 0%,#1E3A5F 60%,#2563EB 100%);
border-radius:16px;padding:2.5rem 2.5rem 2rem;margin-bottom:1.5rem;">
<div style="font-size:0.75rem;font-weight:700;color:#93C5FD;
text-transform:uppercase;letter-spacing:0.1em;margin-bottom:8px;">
CRPF Hackathon · Theme 3</div>
<h1 style="margin:0;font-size:2.2rem;font-weight:800;color:#FFFFFF;
letter-spacing:-0.02em;line-height:1.2;">⚖️ TenderIQ</h1>
<p style="margin:10px 0 0;font-size:1rem;color:#CBD5E1;max-width:600px;line-height:1.6;">
Explainable AI for Government Tender Evaluation — automated eligibility
assessment with criterion-level evidence, three-tier OCR, and a complete
compliance audit trail.</p>
</div>""",
unsafe_allow_html=True,
)
# KPIs
criteria_count = len(st.session_state.get("criteria") or [])
verdicts = st.session_state.get("verdicts", {})
checked = sum(1 for bv in verdicts.values() for _ in bv)
audit_count = len(audit.query())
c1, c2, c3, c4 = st.columns(4)
with c1: stat_card(criteria_count, "Criteria Extracted", "#3B82F6")
with c2: stat_card(len(verdicts), "Bidders Evaluated", "#22C55E")
with c3: stat_card(checked, "Criteria Checked", "#8B5CF6")
with c4: stat_card(audit_count, "Audit Entries", "#F59E0B")
st.divider()
# Pipeline stages
st.markdown(
'<p style="font-size:1rem;font-weight:700;color:var(--text-color);">'
'How it works</p>',
unsafe_allow_html=True,
)
stages = [
("#3B82F6", "rgba(37,99,235,0.08)", "1", "Extract Criteria",
"DeepSeek reads the tender PDF and returns structured JSON for each criterion — "
"category, mandatory flag, rule, source clause, and query hints."),
("#8B5CF6", "rgba(124,58,237,0.08)", "2", "Three-Tier OCR",
"📄 PyMuPDF for typed PDFs · 🔍 Tesseract for scans · 👁 DeepSeek Vision LLM "
"when Tesseract confidence < 65%. Every page records its tier and confidence."),
("#22C55E", "rgba(34,197,94,0.08)", "3", "Evaluate per Criterion",
"Semantic search retrieves the top-k evidence chunks. DeepSeek returns a verdict "
"with combined confidence. Safety rule: borderline not-eligible is always "
"downgraded to needs-review."),
("#F59E0B", "rgba(245,158,11,0.08)", "4", "Human Review & Audit",
"Flagged verdicts surface with full evidence and source citations. Every action "
"is logged to SQLite with timestamp, model version, actor, and payload."),
]
cols = st.columns(4)
for col, (accent, bg, num, title, body) in zip(cols, stages):
with col:
st.markdown(
f"""<div style="background:{bg};border:1px solid {accent}33;
border-radius:12px;padding:18px 16px;height:100%;">
<div style="display:flex;align-items:center;gap:8px;margin-bottom:10px;">
<div style="background:{accent};color:#fff;border-radius:50%;
width:24px;height:24px;display:flex;align-items:center;
justify-content:center;font-size:0.75rem;font-weight:700;
flex-shrink:0;">{num}</div>
<span style="font-weight:700;font-size:0.9rem;color:var(--text-color);">
{title}</span>
</div>
<p style="margin:0;font-size:0.82rem;color:var(--text-color);
opacity:0.75;line-height:1.6;">{body}</p>
</div>""",
unsafe_allow_html=True,
)
st.divider()
col1, col2 = st.columns(2)
with col1:
with st.container(border=True):
st.markdown("**🚀 Pre-computed Demo**")
st.caption("Instantly load realistic results for all 3 bidders — no API key needed.")
if st.button("Load Pre-computed Demo", type="primary", use_container_width=True):
from core.fallback import load_criteria as lc, load_evaluation
criteria = lc()
st.session_state["criteria"] = [c.model_dump() for c in criteria]
vd: dict = {}
for bid in BIDDER_NAMES:
vd[bid] = [load_evaluation(bid, c.id).model_dump() for c in criteria]
st.session_state["verdicts"] = vd
st.success("Loaded — navigate to Bidder Evaluation or Interpretability.")
st.rerun()
with col2:
with st.container(border=True):
st.markdown("**⚡ Live Pipeline**")
st.caption("Set DEEPSEEK_API_KEY in .env, then use the Tender Analysis tab.")
st.info("Sidebar shows 🟢 when the API is reachable.")
|