|
|
|
|
|
LOGO_URL = "https://raw.githubusercontent.com/Decoding-Data-Science/Omantel/main/Omantel_Logo%20(1).png" |
|
|
|
|
|
CUSTOM_CSS = """ |
|
|
:root{ |
|
|
--bg:#0a0f1c; |
|
|
--panel:#0f1629f2; |
|
|
--stroke:#1d2740; |
|
|
--muted:#96a3be; |
|
|
--brand:#2e90fa; |
|
|
--accent:#f59e0b; |
|
|
--ok:#10b981; |
|
|
--danger:#ef4444; |
|
|
} |
|
|
.gradio-container{ |
|
|
font-family: Inter, ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, "Helvetica Neue", Arial !important; |
|
|
} |
|
|
body{ |
|
|
background: |
|
|
radial-gradient(1100px 500px at 10% -10%, #2e90fa22, transparent 70%), |
|
|
radial-gradient(1000px 500px at 110% 10%, #f59e0b18, transparent 60%), |
|
|
linear-gradient(180deg, var(--bg) 0%, #0a0f1c 100%); |
|
|
} |
|
|
.header{ |
|
|
position:relative; |
|
|
padding:18px 12px 8px; |
|
|
border-bottom:1px solid var(--stroke); |
|
|
display:grid; |
|
|
grid-template-columns: 140px 1fr 140px; |
|
|
align-items:center; |
|
|
gap:12px; |
|
|
} |
|
|
.header .logo img{height:50px;object-fit:contain; filter: drop-shadow(0 4px 14px rgba(0,0,0,.25));} |
|
|
.title{ |
|
|
text-align:center; color:#e5e7eb; |
|
|
} |
|
|
.title h1{ |
|
|
margin:0; font-weight:800; font-size:1.7rem; letter-spacing:.2px; |
|
|
} |
|
|
.title p{ |
|
|
margin:6px 0 0; color:var(--muted); font-size:.95rem; |
|
|
} |
|
|
.pill{ |
|
|
display:inline-flex; align-items:center; gap:8px; |
|
|
padding:6px 10px; border-radius:999px; font-size:12px; |
|
|
background:linear-gradient(180deg,#0d1425,#0a1222); |
|
|
border:1px solid var(--stroke); color:#cbd5e1; |
|
|
} |
|
|
.pill .dot{width:8px;height:8px;border-radius:999px;background:var(--ok); box-shadow:0 0 0 4px #10b98126;} |
|
|
.panel{ |
|
|
background:var(--panel); border:1px solid var(--stroke); |
|
|
border-radius:16px; padding:16px; backdrop-filter: blur(10px); |
|
|
box-shadow:0 12px 36px rgba(0,0,0,.35), inset 0 1px 0 rgba(255,255,255,.03); |
|
|
} |
|
|
.hero{ |
|
|
position:relative; border-radius:18px; padding:18px; margin-top:8px; |
|
|
border:1px solid #25314e; |
|
|
background: |
|
|
radial-gradient(800px 200px at 12% -12%, rgba(46, 144, 250, .24), transparent 70%), |
|
|
radial-gradient(700px 140px at 90% 0%, rgba(245, 158, 11, .14), transparent 60%), |
|
|
linear-gradient(180deg, #0d1428, #0c1224); |
|
|
} |
|
|
.hero h2{margin:0 0 6px;color:#e5e7eb;font-weight:700; font-size:1.1rem;} |
|
|
.hero p{margin:0;color:var(--muted);font-size:.95rem;} |
|
|
.label, label{color:#dbe3f0 !important; font-weight:600 !important;} |
|
|
.helper{font-size:12px;color:#9aa5b1;margin-top:6px;} |
|
|
.chips{display:flex; gap:8px; flex-wrap:wrap; margin-top:8px;} |
|
|
.chip{ |
|
|
cursor:pointer; user-select:none; |
|
|
padding:8px 12px; border-radius:12px; font-size:.9rem; |
|
|
background:#0b1222; border:1px solid #273043; color:#cbd5e1; |
|
|
} |
|
|
.chip:hover{border-color:#35507a} |
|
|
.actions{display:flex; gap:8px; flex-wrap:wrap;} |
|
|
button.primary{ |
|
|
background:linear-gradient(180deg,#3b82f6,#1e40af); color:white; border:1px solid #1e3a8a; |
|
|
} |
|
|
button.secondary{ |
|
|
background:#0b1222; color:#cbd5e1; border:1px solid #273043; |
|
|
} |
|
|
.footer{ text-align:center; font-size:12px; color:#94a3b8; padding:12px 0 0;} |
|
|
#answer_box{min-height:200px;} |
|
|
.small-note{font-size:12px;color:#9aa5b1;} |
|
|
kbd{ |
|
|
background:#0f172a; color:#e5e7eb; border:1px solid #334155; |
|
|
border-bottom-width:2px; padding:2px 6px; border-radius:6px; font-size:.8em; |
|
|
} |
|
|
""" |
|
|
|
|
|
with gr.Blocks(css=CUSTOM_CSS, title="Mulhim • Omantel Knowledge Assistant (Aurora)") as demo: |
|
|
|
|
|
with gr.Row(elem_classes="header"): |
|
|
with gr.Column(scale=0, elem_classes="logo"): |
|
|
gr.HTML(f''' |
|
|
<div class="pill"><span class="dot"></span> Online • Mulhim</div> |
|
|
<br><img src="{LOGO_URL}" alt="Omantel Logo"> |
|
|
''') |
|
|
with gr.Column(scale=1, elem_classes="title"): |
|
|
gr.HTML(""" |
|
|
<h1>Mulhim — Omantel Knowledge Assistant</h1> |
|
|
<p>Answers are <b>strictly</b> grounded in your indexed PDF(s). If it’s not in the file, you’ll be told.</p> |
|
|
""") |
|
|
with gr.Column(scale=0): |
|
|
gr.HTML("") |
|
|
|
|
|
|
|
|
gr.HTML(""" |
|
|
<div class="hero panel"> |
|
|
<h2>How it works</h2> |
|
|
<p>Ask about internal applications, steps, or focal points. Mulhim cites only what's inside your indexed document.</p> |
|
|
</div> |
|
|
""") |
|
|
|
|
|
with gr.Row(): |
|
|
|
|
|
with gr.Column(scale=7): |
|
|
with gr.Group(elem_classes="panel"): |
|
|
inp = gr.Textbox( |
|
|
label="Your question", |
|
|
placeholder="e.g., Procurement System — steps and owner contact?", |
|
|
lines=3, |
|
|
autofocus=True |
|
|
) |
|
|
with gr.Row(elem_classes="actions"): |
|
|
ask_btn = gr.Button("Ask Mulhim", variant="primary", elem_classes="primary") |
|
|
clear_btn = gr.Button("Clear", variant="secondary", elem_classes="secondary") |
|
|
gr.Markdown('<div class="helper">Tip: Mention the process or department for best results. Press <kbd>Enter</kbd> to submit.</div>') |
|
|
|
|
|
|
|
|
with gr.Row(elem_classes="chips"): |
|
|
chip1 = gr.Button("Focal point • Technology Strategy", elem_id=None, elem_classes="chip", variant="secondary") |
|
|
chip2 = gr.Button("Steps • New vendor contract", elem_classes="chip", variant="secondary") |
|
|
chip3 = gr.Button("Process • Customer complaints (CEX)", elem_classes="chip", variant="secondary") |
|
|
chip4 = gr.Button("Finance • Budget reallocation", elem_classes="chip", variant="secondary") |
|
|
|
|
|
with gr.Group(elem_classes="panel"): |
|
|
out = gr.Textbox( |
|
|
label="Answer", |
|
|
lines=14, |
|
|
elem_id="answer_box", |
|
|
show_copy_button=True |
|
|
) |
|
|
gr.Markdown('<div class="small-note">If the answer isn’t found in the PDF, you’ll see: <i>“I couldn’t find that in the document.”</i></div>') |
|
|
|
|
|
|
|
|
with gr.Column(scale=5): |
|
|
with gr.Group(elem_classes="panel"): |
|
|
gr.Markdown("**Quick examples**") |
|
|
examples = gr.Examples( |
|
|
examples=[ |
|
|
["Who is the focal point for Technology Strategy and how can I reach them?"], |
|
|
["I need to submit a new vendor contract. Which application and steps?"], |
|
|
["For customer complaints (CEX), what's the process and owner contact?"], |
|
|
["Finance: how to request a budget reallocation and who to email?"], |
|
|
], |
|
|
inputs=inp, |
|
|
) |
|
|
with gr.Group(elem_classes="panel"): |
|
|
gr.Markdown("**Notes**") |
|
|
gr.Markdown( |
|
|
"- Answers are sourced only from the indexed PDF/document.\n" |
|
|
"- If information isn’t found, Mulhim will say so.\n" |
|
|
"- For department-specific queries, the related contact is shown if present in the doc." |
|
|
) |
|
|
|
|
|
gr.Markdown(f'<div class="footer">Pinecone index: <b>{index_name}</b> • Namespace: <b>{PINECONE_NAMESPACE}</b> • LlamaIndex + Pinecone</div>') |
|
|
|
|
|
|
|
|
ask_btn.click(fn=query_doc, inputs=inp, outputs=out) |
|
|
inp.submit(fn=query_doc, inputs=inp, outputs=out) |
|
|
clear_btn.click(lambda: ("", ""), inputs=None, outputs=[inp, out]) |
|
|
|
|
|
|
|
|
chip1.click(lambda: "Who is the focal point for Technology Strategy and how can I reach them?", outputs=inp).then(query_doc, inp, out) |
|
|
chip2.click(lambda: "I need to submit a new vendor contract. Which application and steps?", outputs=inp).then(query_doc, inp, out) |
|
|
chip3.click(lambda: "For customer complaints (CEX), what's the process and owner contact?", outputs=inp).then(query_doc, inp, out) |
|
|
chip4.click(lambda: "Finance: how to request a budget reallocation and who to email?", outputs=inp).then(query_doc, inp, out) |
|
|
|
|
|
demo.launch() |
|
|
|