Spaces:
Configuration error
Configuration error
Download app.py from Sumayyea/DigitalKPAssistant: direct link, hf CLI and curl.
- Browser
- Download file 5.87 kB
-
https://huggingface.co/spaces/Sumayyea/DigitalKPAssistant/resolve/main/app.py
- Command line
-
hf download hf://spaces/Sumayyea/DigitalKPAssistant/app.py
-
curl -L -o app.py https://huggingface.co/spaces/Sumayyea/DigitalKPAssistant/resolve/main/app.py
5.87 kB
| """ | |
| app.py — Streamlit interface for the Digital KP 2030 assistant. | |
| Run locally: streamlit run app.py | |
| On Hugging Face Spaces this file is detected and launched automatically. | |
| """ | |
| from __future__ import annotations | |
| import streamlit as st | |
| from rag import Assistant, EXAMPLE_QUESTIONS, LLM_MODEL | |
| st.set_page_config( | |
| page_title="Digital KP 2030 Assistant", | |
| page_icon="📘", | |
| layout="centered", | |
| initial_sidebar_state="expanded", | |
| ) | |
| # -------------------------------------------------------------------------- | |
| # Styling — a restrained navy palette matching the source document | |
| # -------------------------------------------------------------------------- | |
| st.markdown(""" | |
| <style> | |
| .block-container { padding-top: 2.2rem; max-width: 52rem; } | |
| .kp-header { | |
| background: #0B2545; border-radius: 10px; | |
| padding: 1.4rem 1.6rem; margin-bottom: 1.2rem; | |
| } | |
| .kp-header h1 { color: #fff; font-size: 1.55rem; margin: 0 0 .3rem 0; } | |
| .kp-header p { color: #8AB6E8; font-size: .92rem; margin: 0; } | |
| .kp-badge { | |
| display: inline-block; background: #EAF1FA; color: #1B5299; | |
| border: 1px solid #D3E1F2; border-radius: 999px; | |
| padding: .12rem .6rem; font-size: .74rem; margin-right: .3rem; | |
| } | |
| .kp-src { | |
| background: #F5F9FD; border: 1px solid #D3E1F2; border-radius: 8px; | |
| padding: .7rem .9rem; margin-bottom: .55rem; font-size: .86rem; | |
| } | |
| .kp-src b { color: #1B5299; } | |
| </style> | |
| """, unsafe_allow_html=True) | |
| st.markdown(""" | |
| <div class="kp-header"> | |
| <h1>Digital KP 2030 Assistant</h1> | |
| <p>Answers drawn only from the Khyber Pakhtunkhwa Digital Transformation | |
| Policy & Roadmap 2030, with page citations.</p> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| # -------------------------------------------------------------------------- | |
| # Load the index once per session | |
| # -------------------------------------------------------------------------- | |
| def load_assistant() -> Assistant: | |
| return Assistant() | |
| try: | |
| assistant = load_assistant() | |
| except FileNotFoundError as exc: | |
| st.error(str(exc)) | |
| st.stop() | |
| except Exception as exc: # noqa: BLE001 | |
| st.error(f"Could not start the assistant: {type(exc).__name__}: {exc}") | |
| st.stop() | |
| # -------------------------------------------------------------------------- | |
| # Sidebar | |
| # -------------------------------------------------------------------------- | |
| with st.sidebar: | |
| st.subheader("About") | |
| st.write( | |
| "This assistant retrieves passages from the published Digital KP 2030 " | |
| "document and answers strictly from them. If something is not in the " | |
| "document, it says so rather than guessing." | |
| ) | |
| meta = assistant.meta | |
| st.markdown( | |
| f'<span class="kp-badge">{meta["pages"]} pages</span>' | |
| f'<span class="kp-badge">{meta["chunks"]} chunks</span>' | |
| f'<span class="kp-badge">{LLM_MODEL.split("/")[-1]}</span>', | |
| unsafe_allow_html=True, | |
| ) | |
| st.divider() | |
| st.subheader("Try a question") | |
| for q in EXAMPLE_QUESTIONS[:6]: | |
| if st.button(q, key=f"ex-{q}", use_container_width=True): | |
| st.session_state.pending = q | |
| st.divider() | |
| if st.button("Clear conversation", use_container_width=True): | |
| st.session_state.messages = [] | |
| st.rerun() | |
| st.caption( | |
| "Source: Khyber Pakhtunkhwa Digital Transformation Board. " | |
| "Verify anything consequential against the published PDF." | |
| ) | |
| # -------------------------------------------------------------------------- | |
| # Chat state | |
| # -------------------------------------------------------------------------- | |
| if "messages" not in st.session_state: | |
| st.session_state.messages = [] | |
| for msg in st.session_state.messages: | |
| with st.chat_message(msg["role"]): | |
| st.markdown(msg["content"]) | |
| if msg.get("sources"): | |
| with st.expander(f"Sources — {len(msg['sources'])} passage(s)"): | |
| for s in msg["sources"]: | |
| preview = s["text"][:420] + ("…" if len(s["text"]) > 420 else "") | |
| st.markdown( | |
| f'<div class="kp-src"><b>Page {s["page"]}</b> ' | |
| f'· relevance {s["score"]:.2f}<br>{preview}</div>', | |
| unsafe_allow_html=True, | |
| ) | |
| typed = st.chat_input("Ask about goals, pillars, KPIs, governance or timelines…") | |
| question = typed or st.session_state.pop("pending", None) | |
| if question: | |
| st.session_state.messages.append({"role": "user", "content": question}) | |
| with st.chat_message("user"): | |
| st.markdown(question) | |
| with st.chat_message("assistant"): | |
| with st.spinner("Searching the document…"): | |
| history = [ | |
| {"role": m["role"], "content": m["content"]} | |
| for m in st.session_state.messages[:-1] | |
| ] | |
| result = assistant.answer(question, history=history) | |
| st.markdown(result["answer"]) | |
| if result["error"] and result["error"] != "missing_key": | |
| st.warning(f"Model error: {result['error']}") | |
| sources = [ | |
| {"page": s.page, "text": s.text, "score": s.score} | |
| for s in result["sources"] | |
| ] | |
| if sources: | |
| with st.expander(f"Sources — {len(sources)} passage(s)"): | |
| for s in sources: | |
| preview = s["text"][:420] + ("…" if len(s["text"]) > 420 else "") | |
| st.markdown( | |
| f'<div class="kp-src"><b>Page {s["page"]}</b> ' | |
| f'· relevance {s["score"]:.2f}<br>{preview}</div>', | |
| unsafe_allow_html=True, | |
| ) | |
| st.session_state.messages.append({ | |
| "role": "assistant", | |
| "content": result["answer"], | |
| "sources": sources, | |
| }) | |