| import streamlit as st |
| import tempfile |
| import uuid |
| from langchain_community.document_loaders import PyMuPDFLoader |
| from langchain_text_splitters import RecursiveCharacterTextSplitter |
| from langchain_huggingface import HuggingFaceEmbeddings |
| from langchain_chroma import Chroma |
|
|
| from graph.workflow import app |
|
|
| @st.cache_resource |
| def load_embedding_model(): |
| return HuggingFaceEmbeddings( |
| model_name="BAAI/bge-base-en-v1.5" |
| ) |
|
|
| embedding_model = load_embedding_model() |
|
|
| st.set_page_config( |
| page_title="Agentic Corrective RAG", |
| page_icon="🤖", |
| layout="wide" |
| ) |
|
|
| st.title("🤖 Agentic Corrective RAG") |
|
|
| uploaded_files = st.sidebar.file_uploader( |
| "Upload PDF files", |
| type=["pdf"], |
| accept_multiple_files=True |
| ) |
|
|
| |
| uploaded_names = sorted( |
| [file.name for file in uploaded_files] |
| ) if uploaded_files else [] |
|
|
|
|
| if ( |
| uploaded_files and |
| st.session_state.get("uploaded_files") != uploaded_names |
| ): |
| with st.spinner("Processing PDFs..."): |
|
|
| documents = [] |
|
|
| for uploaded_file in uploaded_files: |
|
|
| with tempfile.NamedTemporaryFile( |
| delete=False, |
| suffix=".pdf" |
| ) as tmp_file: |
|
|
| uploaded_file.seek(0) |
|
|
| tmp_file.write( |
| uploaded_file.getvalue() |
| ) |
|
|
| tmp_path = tmp_file.name |
|
|
| loader = PyMuPDFLoader(tmp_path) |
|
|
| docs = loader.load() |
|
|
| documents.extend(docs) |
|
|
| splitter = RecursiveCharacterTextSplitter( |
| chunk_size=1000, |
| chunk_overlap=200 |
| ) |
|
|
| chunked_docs = splitter.split_documents( |
| documents |
| ) |
|
|
| if "uploaded_vector_db" in st.session_state: |
| try: |
| st.session_state[ |
| "uploaded_vector_db" |
| ].delete_collection() |
| except Exception: |
| pass |
|
|
| del st.session_state[ |
| "uploaded_vector_db" |
| ] |
|
|
| collection_name = ( |
| f"session_{uuid.uuid4().hex[:8]}" |
| ) |
|
|
| uploaded_db = Chroma.from_documents( |
| documents=chunked_docs, |
| embedding=embedding_model, |
| collection_name=collection_name, |
| ) |
|
|
| st.session_state.uploaded_vector_db = uploaded_db |
| st.session_state.uploaded_files = uploaded_names |
|
|
| st.sidebar.success( |
| f"Indexed {len(chunked_docs)} chunks." |
| ) |
| |
|
|
| if "messages" not in st.session_state: |
| st.session_state.messages = [] |
|
|
| for message in st.session_state.messages: |
|
|
| with st.chat_message(message["role"]): |
| st.markdown(message["content"]) |
|
|
| if prompt := st.chat_input("Ask anything..."): |
|
|
| if "uploaded_vector_db" not in st.session_state: |
| st.warning("Please upload at least one PDF first.") |
| st.stop() |
|
|
| st.session_state.messages.append( |
| { |
| "role": "user", |
| "content": prompt |
| } |
| ) |
|
|
| with st.chat_message("user"): |
| st.markdown(prompt) |
|
|
| with st.chat_message("assistant"): |
|
|
| with st.spinner("Thinking..."): |
|
|
| result = app.invoke( |
| { |
| "question": prompt, |
|
|
| |
| "vector_db": st.session_state.get( |
| "uploaded_vector_db", |
| None |
| ) |
| } |
| ) |
|
|
| response = result["answer"] |
|
|
| st.markdown(response) |
|
|
| if result.get("route") == "local": |
| st.caption( |
| f"Source: Uploaded Document | Retrieval Score: {result['retrieval_score']:.3f}" |
| ) |
|
|
| elif result.get("route") == "web": |
| st.caption( |
| f"Source: Web Search | Retrieval Score: {result['retrieval_score']:.3f}" |
| ) |
|
|
| st.session_state.messages.append( |
| { |
| "role": "assistant", |
| "content": response |
| } |
| ) |