Insurio / app.py
VGreatVig07's picture
Upload 4 files
4a20a8f verified
Raw
History Blame Contribute Delete
6.94 kB
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 ────────────────────────────────
@st.cache_resource(show_spinner="Building knowledge base from PDF...")
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.")