Spaces:
Sleeping
Sleeping
| import os | |
| import streamlit as st | |
| from dotenv import load_dotenv | |
| load_dotenv() | |
| from rag import build_vectorstore, retrieve_context | |
| from sarvam_client import get_llm_response, text_to_speech, transcribe_audio | |
| st.set_page_config(page_title="Insurance Assistant", page_icon="π‘οΈ", layout="wide") | |
| st.title("Insurance Assistant π‘οΈ") | |
| # ββ Sidebar ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| with st.sidebar: | |
| st.header("Policy Document") | |
| uploaded_file = st.file_uploader( | |
| "Upload your insurance policy PDF", | |
| type=["pdf"], | |
| help="Upload a PDF policy document to enable Q&A", | |
| ) | |
| st.divider() | |
| st.subheader("Your Profile (Optional)") | |
| age = st.number_input("Your Age", min_value=18, max_value=80, value=30) | |
| dependents = st.selectbox("Dependents", ["None", "Spouse", "Spouse + Kids", "Parents"]) | |
| budget = st.selectbox("Monthly Budget", ["Under βΉ500", "βΉ500-1000", "βΉ1000-2000", "βΉ2000+"]) | |
| st.divider() | |
| st.markdown("**How to use**") | |
| st.markdown("1. Upload a policy PDF above\n2. Type or record your question\n3. Get instant answers with audio") | |
| # ββ Session state init βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| if "messages" not in st.session_state: | |
| st.session_state.messages = [] | |
| if "conversation_history" not in st.session_state: | |
| st.session_state.conversation_history = [] | |
| if "pdf_hash" not in st.session_state: | |
| st.session_state.pdf_hash = None | |
| # ββ Build / cache vector store when PDF changes ββββββββββββββββββββββββββββββββ | |
| def get_vectorstore(pdf_bytes: bytes, file_hash: int): | |
| return build_vectorstore(pdf_bytes) | |
| vectorstore = None | |
| if uploaded_file is not None: | |
| pdf_bytes = uploaded_file.read() | |
| file_hash = hash(pdf_bytes) | |
| try: | |
| vectorstore = get_vectorstore(pdf_bytes, file_hash) | |
| if st.session_state.pdf_hash != file_hash: | |
| st.session_state.pdf_hash = file_hash | |
| st.session_state.messages = [] | |
| st.session_state.conversation_history = [] | |
| st.sidebar.success("PDF loaded! Knowledge base ready.") | |
| except ValueError as e: | |
| st.sidebar.error(f"PDF error: {e}") | |
| except Exception as e: | |
| st.sidebar.error(f"Failed to process PDF: {e}") | |
| # ββ Render chat history ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| for msg in st.session_state.messages: | |
| with st.chat_message(msg["role"]): | |
| st.markdown(msg["content"]) | |
| if msg["role"] == "assistant": | |
| if msg.get("audio"): | |
| st.audio(msg["audio"], format="audio/wav", autoplay=False) | |
| if msg.get("chunks"): | |
| with st.expander("π Sources from policy document"): | |
| for i, chunk in enumerate(msg["chunks"]): | |
| st.markdown(f"**Chunk {i+1}:**") | |
| st.caption(chunk[:300] + "..." if len(chunk) > 300 else chunk) | |
| # ββ Helper: process a user query end-to-end βββββββββββββββββββββββββββββββββββ | |
| def process_query(user_text: str): | |
| if not user_text.strip(): | |
| return | |
| st.session_state.messages.append({"role": "user", "content": user_text}) | |
| with st.chat_message("user"): | |
| st.markdown(user_text) | |
| if vectorstore is None: | |
| with st.chat_message("assistant"): | |
| msg = "Please upload a policy PDF in the sidebar to get started." | |
| st.markdown(msg) | |
| st.session_state.messages.append({"role": "assistant", "content": msg, "audio": None, "chunks": []}) | |
| return | |
| with st.chat_message("assistant"): | |
| with st.spinner("Thinking..."): | |
| try: | |
| context, chunks = retrieve_context(vectorstore, user_text) | |
| user_profile = ( | |
| f"\n\nUser Profile: Age {age}, Dependents: {dependents}, " | |
| f"Budget: {budget}/month" | |
| ) | |
| full_context = context + user_profile | |
| response_text = get_llm_response( | |
| user_text, | |
| full_context, | |
| conversation_history=st.session_state.conversation_history, | |
| ) | |
| except RuntimeError as e: | |
| st.error(f"Error getting response: {e}") | |
| return | |
| st.markdown(response_text) | |
| with st.expander("π Sources from policy document"): | |
| for i, chunk in enumerate(chunks): | |
| st.markdown(f"**Chunk {i+1}:**") | |
| st.caption(chunk[:300] + "..." if len(chunk) > 300 else chunk) | |
| audio_bytes = None | |
| try: | |
| audio_bytes = text_to_speech(response_text, user_query=user_text) | |
| st.audio(audio_bytes, format="audio/wav", autoplay=True) | |
| except RuntimeError as e: | |
| st.warning(f"Audio generation failed: {e}") | |
| # Update multi-turn conversation history | |
| st.session_state.conversation_history.append({"role": "user", "content": user_text}) | |
| st.session_state.conversation_history.append({"role": "assistant", "content": response_text}) | |
| st.session_state.messages.append({ | |
| "role": "assistant", | |
| "content": response_text, | |
| "audio": audio_bytes, | |
| "chunks": chunks, | |
| }) | |
| # ββ Unified chat input (text + mic inside one bar, Streamlit 1.57+) βββββββββββ | |
| prompt = st.chat_input( | |
| "Ask about your insurance policy...", | |
| accept_audio=True, | |
| audio_sample_rate=16000, | |
| ) | |
| if prompt: | |
| user_text = "" | |
| if isinstance(prompt, str): | |
| user_text = prompt.strip() | |
| else: | |
| user_text = (prompt.text or "").strip() | |
| if not user_text and prompt.audio is not None: | |
| wav_bytes = prompt.audio.getvalue() | |
| audio_key = hash(wav_bytes) | |
| if st.session_state.get("last_audio_key") != audio_key: | |
| st.session_state["last_audio_key"] = audio_key | |
| with st.spinner("Transcribing audio..."): | |
| try: | |
| user_text = transcribe_audio(wav_bytes).strip() | |
| except RuntimeError as e: | |
| st.error(f"Transcription failed: {e}") | |
| if user_text: | |
| process_query(user_text) | |
| elif isinstance(prompt, str) is False and prompt.audio is not None: | |
| st.warning("Could not transcribe audio. Please try again or type your question.") | |