demo2 / streamlit_app_N.py
Dinesh310's picture
Create streamlit_app_N.py
1ec32f4 verified
import uuid
import streamlit as st
from langchain_core.messages import AIMessage, HumanMessage, ToolMessage
from src.langraph_rag_backend import (
chatbot,
ingest_pdf,
retrieve_all_threads,
thread_document_metadata,
)
# =========================== Utilities ===========================
def generate_thread_id():
return uuid.uuid4()
def reset_chat():
thread_id = generate_thread_id()
st.session_state["thread_id"] = thread_id
add_thread(thread_id)
st.session_state["message_history"] = []
def add_thread(thread_id):
if thread_id not in st.session_state["chat_threads"]:
st.session_state["chat_threads"].append(thread_id)
def load_conversation(thread_id):
state = chatbot.get_state(config={"configurable": {"thread_id": thread_id}})
return state.values.get("messages", [])
# ======================= Session Initialization ===================
if "message_history" not in st.session_state:
st.session_state["message_history"] = []
if "thread_id" not in st.session_state:
st.session_state["thread_id"] = generate_thread_id()
if "chat_threads" not in st.session_state:
st.session_state["chat_threads"] = retrieve_all_threads()
if "ingested_docs" not in st.session_state:
st.session_state["ingested_docs"] = {}
add_thread(st.session_state["thread_id"])
thread_key = str(st.session_state["thread_id"])
thread_docs = st.session_state["ingested_docs"].setdefault(thread_key, {})
threads = st.session_state["chat_threads"][::-1]
selected_thread = None
# ============================ Sidebar ============================
st.sidebar.title("LangGraph PDF Chatbot")
st.sidebar.markdown(f"**Thread ID:** `{thread_key}`")
if st.sidebar.button("New Chat", use_container_width=True):
reset_chat()
st.rerun()
if thread_docs:
latest_doc = list(thread_docs.values())[-1]
st.sidebar.success(
f"Using `{latest_doc.get('filename')}` "
f"({latest_doc.get('chunks')} chunks from {latest_doc.get('documents')} pages)"
)
else:
st.sidebar.info("No PDF indexed yet.")
uploaded_pdfs = st.sidebar.file_uploader("Upload a PDF for this chat", type=["pdf"], accept_multiple_files=True)
if uploaded_pdfs:
for uploaded_pdf in uploaded_pdfs:
if uploaded_pdf.name in thread_docs:
st.sidebar.info(f"`{uploaded_pdf.name}` already processed for this chat.")
else:
with st.sidebar.status("Indexing PDF…", expanded=True) as status_box:
summary = ingest_pdf(
uploaded_pdf.getvalue(),
thread_id=thread_key,
filename=uploaded_pdf.name,
)
thread_docs[uploaded_pdf.name] = summary
status_box.update(label="✅ PDF indexed", state="complete", expanded=False)
st.sidebar.subheader("Past conversations")
if not threads:
st.sidebar.write("No past conversations yet.")
else:
for thread_id in threads:
if st.sidebar.button(str(thread_id), key=f"side-thread-{thread_id}"):
selected_thread = thread_id
# ============================ Main Layout ========================
st.title("Multi Utility Chatbot")
# Chat area
for message in st.session_state["message_history"]:
with st.chat_message(message["role"]):
st.text(message["content"])
user_input = st.chat_input("Ask about your document or use tools")
if user_input:
st.session_state["message_history"].append({"role": "user", "content": user_input})
with st.chat_message("user"):
st.text(user_input)
CONFIG = {
"configurable": {"thread_id": thread_key},
"metadata": {"thread_id": thread_key},
"run_name": "chat_turn",
}
with st.chat_message("assistant"):
status_holder = {"box": None}
def ai_only_stream():
for message_chunk, _ in chatbot.stream(
{"messages": [HumanMessage(content=user_input)]},
config=CONFIG,
stream_mode="messages",
):
if isinstance(message_chunk, ToolMessage):
tool_name = getattr(message_chunk, "name", "tool")
if status_holder["box"] is None:
status_holder["box"] = st.status(
f"🔧 Using `{tool_name}` …", expanded=True
)
else:
status_holder["box"].update(
label=f"🔧 Using `{tool_name}` …",
state="running",
expanded=True,
)
if isinstance(message_chunk, AIMessage):
yield message_chunk.content
ai_message = st.write_stream(ai_only_stream())
if status_holder["box"] is not None:
status_holder["box"].update(
label="✅ Tool finished", state="complete", expanded=False
)
st.session_state["message_history"].append(
{"role": "assistant", "content": ai_message}
)
doc_meta = thread_document_metadata(thread_key)
if doc_meta:
st.caption(
f"Document indexed: {doc_meta.get('filename')} "
f"(chunks: {doc_meta.get('chunks')}, pages: {doc_meta.get('documents')})"
)
st.divider()
if selected_thread:
st.session_state["thread_id"] = selected_thread
messages = load_conversation(selected_thread)
temp_messages = []
for msg in messages:
role = "user" if isinstance(msg, HumanMessage) else "assistant"
temp_messages.append({"role": role, "content": msg.content})
st.session_state["message_history"] = temp_messages
st.session_state["ingested_docs"].setdefault(str(selected_thread), {})
st.rerun()