|
|
|
|
|
import sys, os |
|
|
|
|
|
|
|
|
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) |
|
|
ROOT_DIR = os.path.abspath(os.path.join(CURRENT_DIR, "..")) |
|
|
if ROOT_DIR not in sys.path: |
|
|
sys.path.insert(0, ROOT_DIR) |
|
|
|
|
|
from pathlib import Path |
|
|
import streamlit as st |
|
|
from apps.rag_pipeline import rag_pipeline |
|
|
from apps.core.config import UPLOAD_DIR, APP_NAME, DESCRIPTION |
|
|
|
|
|
|
|
|
UPLOAD_DIR.mkdir(parents=True, exist_ok=True) |
|
|
|
|
|
|
|
|
st.set_page_config( |
|
|
page_title=APP_NAME, |
|
|
page_icon="📘", |
|
|
layout="wide", |
|
|
) |
|
|
|
|
|
|
|
|
st.markdown(""" |
|
|
<style> |
|
|
.main { |
|
|
background-color: #f8fafc; |
|
|
padding: 1rem 3rem; |
|
|
} |
|
|
|
|
|
.stApp { |
|
|
max-width: 100%; |
|
|
margin: 0; |
|
|
padding: 0; |
|
|
} |
|
|
|
|
|
div[data-testid="stMarkdownContainer"] p { |
|
|
font-size: 16px !important; |
|
|
color: #1f2937; |
|
|
} |
|
|
|
|
|
.stButton>button { |
|
|
background-color: #2563eb; |
|
|
color: white; |
|
|
border-radius: 8px; |
|
|
padding: 0.6em 1.2em; |
|
|
font-weight: 500; |
|
|
transition: 0.25s; |
|
|
} |
|
|
.stButton>button:hover { |
|
|
background-color: #1d4ed8; |
|
|
color: white; |
|
|
transform: scale(1.02); |
|
|
} |
|
|
|
|
|
h1, h2, h3 { |
|
|
color: #1e3a8a; |
|
|
} |
|
|
|
|
|
.answer-box { |
|
|
padding: 1em; |
|
|
background-color: #eef2ff; |
|
|
border-left: 5px solid #3b82f6; |
|
|
border-radius: 8px; |
|
|
} |
|
|
|
|
|
section[data-testid="stSidebar"] { |
|
|
background-color: #f1f5f9; |
|
|
border-right: 1px solid #e5e7eb; |
|
|
} |
|
|
|
|
|
/* Compact layout for full-screen fit */ |
|
|
.block-container { |
|
|
padding-top: 1.5rem !important; |
|
|
padding-bottom: 0rem !important; |
|
|
} |
|
|
|
|
|
/* === Option 1: Hide Streamlit’s default 200MB text and replace it === */ |
|
|
div[data-testid="stFileUploaderDropzone"] > small { |
|
|
visibility: hidden !important; |
|
|
} |
|
|
|
|
|
div[data-testid="stFileUploaderDropzone"]::after { |
|
|
content: "Limit 50MB per file • Supported formats: PDF, TXT, MD"; |
|
|
display: block; |
|
|
color: #6b7280; |
|
|
font-size: 0.85rem; |
|
|
text-align: center; |
|
|
margin-top: 6px; |
|
|
} |
|
|
</style> |
|
|
""", unsafe_allow_html=True) |
|
|
|
|
|
|
|
|
st.markdown(f"<h1 style='text-align:center;'>📘 {APP_NAME}</h1>", unsafe_allow_html=True) |
|
|
st.markdown(f"<p style='text-align:center; color:#374151;'>{DESCRIPTION}</p>", unsafe_allow_html=True) |
|
|
st.markdown("---") |
|
|
|
|
|
|
|
|
with st.sidebar: |
|
|
st.header("ℹ️ About This App") |
|
|
st.write(""" |
|
|
This RAG (Retrieval-Augmented Generation) demo lets you: |
|
|
|
|
|
1️⃣ Upload and embed your document |
|
|
2️⃣ Ask questions using an LLM |
|
|
3️⃣ Get context-aware answers instantly |
|
|
""") |
|
|
st.markdown("---") |
|
|
st.caption("Built with ❤️ using Streamlit + LangChain") |
|
|
|
|
|
|
|
|
st.subheader("📤 Step 1: Upload & Ingest Document") |
|
|
|
|
|
uploaded_file = st.file_uploader( |
|
|
"📤 Upload a `.pdf`, `.txt`, or `.md` file (Max size: 50MB)", |
|
|
type=["pdf", "txt", "md"], |
|
|
help="Limit 50MB per file • Supported formats: PDF, TXT, MD", |
|
|
) |
|
|
|
|
|
|
|
|
if uploaded_file: |
|
|
if uploaded_file.size > 50 * 1024 * 1024: |
|
|
st.error("❌ File too large. Please upload a document under 50 MB.") |
|
|
st.stop() |
|
|
|
|
|
dest_path = UPLOAD_DIR / uploaded_file.name |
|
|
dest_path.write_bytes(uploaded_file.read()) |
|
|
st.success(f"✅ File '{uploaded_file.name}' uploaded successfully!") |
|
|
|
|
|
if st.button("🚀 Ingest Document", use_container_width=True): |
|
|
with st.spinner("Embedding and storing document... ⏳"): |
|
|
try: |
|
|
rag_pipeline.ingest_file(dest_path) |
|
|
st.success(f"✅ '{uploaded_file.name}' ingested into vector DB!") |
|
|
except Exception as e: |
|
|
st.error(f"❌ Ingestion failed: {e}") |
|
|
|
|
|
|
|
|
st.markdown("---") |
|
|
st.subheader("💬 Step 2: Ask a Question") |
|
|
|
|
|
query = st.text_area( |
|
|
"Ask something about your uploaded document:", |
|
|
placeholder="e.g. What is the main idea of this text?", |
|
|
height=80, |
|
|
) |
|
|
|
|
|
if st.button("🧠 Get Answer", use_container_width=True): |
|
|
if not query.strip(): |
|
|
st.warning("⚠️ Please enter a question first.") |
|
|
else: |
|
|
with st.spinner("🤔 Thinking..."): |
|
|
try: |
|
|
answer = rag_pipeline.ask(query.strip()) |
|
|
st.markdown( |
|
|
f""" |
|
|
<div class="answer-box"> |
|
|
<strong>🧠 Answer:</strong><br>{answer} |
|
|
</div> |
|
|
""", |
|
|
unsafe_allow_html=True |
|
|
) |
|
|
except Exception as e: |
|
|
st.error(f"❌ Query failed: {e}") |
|
|
|