Spaces:
Sleeping
Sleeping
Download app/ui/streamlit_app.py from ShetyeRupa/DocIntel: direct link, hf CLI and curl.
- Browser
- Download file 15.7 kB
-
https://huggingface.co/spaces/ShetyeRupa/DocIntel/resolve/main/app/ui/streamlit_app.py
- Command line
-
hf download hf://spaces/ShetyeRupa/DocIntel/app/ui/streamlit_app.py
-
curl -L -o streamlit_app.py https://huggingface.co/spaces/ShetyeRupa/DocIntel/resolve/main/app/ui/streamlit_app.py
15.7 kB
| """Main Streamlit application for DocIntel.""" | |
| import sys | |
| import os | |
| # Add the project root to Python path | |
| project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) | |
| if project_root not in sys.path: | |
| sys.path.insert(0, project_root) | |
| import streamlit as st | |
| from app.ingestion.pipeline import IngestionPipeline | |
| from app.agents.orchestrator import DocumentAgent | |
| import tempfile | |
| from datetime import datetime | |
| import plotly.graph_objects as go | |
| # Page config | |
| st.set_page_config( | |
| page_title="DocIntel - Document Intelligence Platform", | |
| page_icon="π", | |
| layout="wide", | |
| initial_sidebar_state="expanded" | |
| ) | |
| # Enhanced Custom CSS | |
| st.markdown(""" | |
| <style> | |
| /* Import Google Fonts */ | |
| @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700;800&display=swap'); | |
| /* Global Styles */ | |
| .stApp { | |
| background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); | |
| font-family: 'Inter', sans-serif; | |
| } | |
| /* Main Header */ | |
| .main-header { | |
| font-size: 3.2rem; | |
| font-weight: 800; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 50%, #f093fb 100%); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| background-clip: text; | |
| margin-bottom: 0.3rem; | |
| letter-spacing: -0.02em; | |
| text-shadow: none; | |
| animation: fadeInDown 0.8s ease-out; | |
| } | |
| .sub-header { | |
| font-size: 1.3rem; | |
| color: #4a4a5a; | |
| margin-bottom: 2rem; | |
| font-weight: 300; | |
| opacity: 0.9; | |
| animation: fadeInUp 0.8s ease-out; | |
| } | |
| /* Animations */ | |
| @keyframes fadeInDown { | |
| from { opacity: 0; transform: translateY(-20px); } | |
| to { opacity: 1; transform: translateY(0); } | |
| } | |
| @keyframes fadeInUp { | |
| from { opacity: 0; transform: translateY(20px); } | |
| to { opacity: 1; transform: translateY(0); } | |
| } | |
| @keyframes pulse { | |
| 0%, 100% { transform: scale(1); } | |
| 50% { transform: scale(1.05); } | |
| } | |
| /* Chat Messages */ | |
| .stChatMessage { | |
| border-radius: 16px !important; | |
| padding: 16px !important; | |
| margin: 8px 0 !important; | |
| box-shadow: 0 4px 12px rgba(0,0,0,0.06) !important; | |
| transition: all 0.3s ease !important; | |
| animation: fadeInUp 0.5s ease-out !important; | |
| } | |
| .stChatMessage:hover { | |
| box-shadow: 0 8px 24px rgba(0,0,0,0.10) !important; | |
| transform: translateY(-2px); | |
| } | |
| /* User Message */ | |
| .chat-message-user { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; | |
| color: white !important; | |
| border-radius: 16px 16px 4px 16px !important; | |
| } | |
| /* Assistant Message */ | |
| .chat-message-assistant { | |
| background: white !important; | |
| border: 1px solid #e8e8e8 !important; | |
| border-radius: 16px 16px 16px 4px !important; | |
| } | |
| /* Source Box */ | |
| .source-box { | |
| background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%); | |
| border-left: 4px solid #667eea; | |
| padding: 14px 16px; | |
| margin: 10px 0; | |
| border-radius: 8px; | |
| font-size: 0.9rem; | |
| transition: all 0.3s ease; | |
| border-left: 4px solid; | |
| border-image: linear-gradient(135deg, #667eea, #764ba2) 1; | |
| } | |
| .source-box:hover { | |
| background: linear-gradient(135deg, #f0f2f6 0%, #e0e4ea 100%); | |
| transform: translateX(4px); | |
| } | |
| /* Metric Cards */ | |
| .metric-card { | |
| background: white; | |
| padding: 20px 24px; | |
| border-radius: 16px; | |
| box-shadow: 0 4px 16px rgba(0,0,0,0.06); | |
| border: 1px solid rgba(102, 126, 234, 0.15); | |
| margin: 10px 0; | |
| transition: all 0.3s ease; | |
| backdrop-filter: blur(10px); | |
| background: rgba(255,255,255,0.9); | |
| } | |
| .metric-card:hover { | |
| transform: translateY(-4px); | |
| box-shadow: 0 8px 32px rgba(0,0,0,0.10); | |
| border-color: #667eea; | |
| } | |
| /* Sidebar */ | |
| .css-1d391kg { | |
| background: linear-gradient(180deg, #ffffff 0%, #f8f9fa 100%) !important; | |
| border-right: 1px solid rgba(102, 126, 234, 0.1) !important; | |
| } | |
| /* Buttons */ | |
| .stButton > button { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; | |
| color: white !important; | |
| border: none !important; | |
| border-radius: 12px !important; | |
| padding: 0.6rem 1.5rem !important; | |
| font-weight: 600 !important; | |
| transition: all 0.3s ease !important; | |
| box-shadow: 0 4px 16px rgba(102, 126, 234, 0.3) !important; | |
| } | |
| .stButton > button:hover { | |
| transform: translateY(-2px) !important; | |
| box-shadow: 0 8px 24px rgba(102, 126, 234, 0.4) !important; | |
| } | |
| .stButton > button:active { | |
| transform: scale(0.96) !important; | |
| } | |
| /* File Uploader */ | |
| .uploadedFile { | |
| border: 2px dashed #667eea !important; | |
| border-radius: 12px !important; | |
| padding: 20px !important; | |
| background: rgba(102, 126, 234, 0.05) !important; | |
| transition: all 0.3s ease !important; | |
| } | |
| .uploadedFile:hover { | |
| background: rgba(102, 126, 234, 0.10) !important; | |
| border-color: #764ba2 !important; | |
| } | |
| /* Expanders */ | |
| .streamlit-expanderHeader { | |
| background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%) !important; | |
| border-radius: 10px !important; | |
| font-weight: 600 !important; | |
| color: #333 !important; | |
| transition: all 0.3s ease !important; | |
| } | |
| .streamlit-expanderHeader:hover { | |
| background: linear-gradient(135deg, #e9ecef 0%, #dee2e6 100%) !important; | |
| } | |
| /* Input Box */ | |
| .stTextInput > div > div > input { | |
| border-radius: 12px !important; | |
| border: 2px solid #e0e0e0 !important; | |
| padding: 12px 16px !important; | |
| font-size: 1rem !important; | |
| transition: all 0.3s ease !important; | |
| } | |
| .stTextInput > div > div > input:focus { | |
| border-color: #667eea !important; | |
| box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.2) !important; | |
| } | |
| /* Chat Input */ | |
| .stChatInput > div { | |
| border-radius: 16px !important; | |
| border: 2px solid #e0e0e0 !important; | |
| padding: 4px !important; | |
| transition: all 0.3s ease !important; | |
| } | |
| .stChatInput > div:focus-within { | |
| border-color: #667eea !important; | |
| box-shadow: 0 0 0 4px rgba(102, 126, 234, 0.15) !important; | |
| } | |
| /* Tabs */ | |
| .stTabs [data-baseweb="tab-list"] { | |
| gap: 8px !important; | |
| } | |
| .stTabs [data-baseweb="tab"] { | |
| border-radius: 8px !important; | |
| padding: 8px 16px !important; | |
| font-weight: 600 !important; | |
| transition: all 0.3s ease !important; | |
| } | |
| .stTabs [aria-selected="true"] { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; | |
| color: white !important; | |
| } | |
| /* Progress Bar */ | |
| .stProgress > div > div > div > div { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; | |
| } | |
| /* Success/Warning/Error Messages */ | |
| .stAlert { | |
| border-radius: 12px !important; | |
| border-left: 4px solid !important; | |
| } | |
| .stAlertSuccess { | |
| border-color: #28a745 !important; | |
| } | |
| .stAlertError { | |
| border-color: #dc3545 !important; | |
| } | |
| .stAlertWarning { | |
| border-color: #ffc107 !important; | |
| } | |
| </style> | |
| """, unsafe_allow_html=True) | |
| # Initialize session state | |
| if "messages" not in st.session_state: | |
| st.session_state.messages = [] | |
| if "agent" not in st.session_state: | |
| st.session_state.agent = DocumentAgent() | |
| if "ingested_count" not in st.session_state: | |
| st.session_state.ingested_count = 0 | |
| if "doc_count" not in st.session_state: | |
| st.session_state.doc_count = 0 | |
| # Sidebar | |
| with st.sidebar: | |
| # Navigation | |
| st.markdown("### π§ Navigation") | |
| st.page_link("streamlit_app.py", label="Chat", icon="π¬") | |
| st.page_link("pages/dashboard.py", label="Dashboard", icon="π") | |
| st.page_link("pages/upload.py", label="Upload", icon="π€") | |
| st.page_link("pages/settings.py", label="Settings", icon="βοΈ") | |
| st.divider() | |
| # Logo and title | |
| st.markdown(""" | |
| <div style="text-align: center; padding: 10px 0;"> | |
| <span style="font-size: 2.5rem;">π</span> | |
| <h2 style="margin: 0; color: #2E86AB;">DocIntel</h2> | |
| <p style="color: #666; margin: 0; font-size: 0.8rem;">Document Intelligence Platform</p> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| st.divider() | |
| # Upload section | |
| st.subheader("π€ Quick Upload") | |
| uploaded_file = st.file_uploader( | |
| "Upload PDF or DOCX", | |
| type=["pdf", "docx", "txt"], | |
| key="sidebar_upload", | |
| help="Supported formats: PDF, DOCX, TXT" | |
| ) | |
| if uploaded_file: | |
| with tempfile.NamedTemporaryFile(delete=False, suffix=f".{uploaded_file.name.split('.')[-1]}") as tmp: | |
| tmp.write(uploaded_file.getvalue()) | |
| tmp_path = tmp.name | |
| if st.button("π Process", type="primary", width="stretch"): | |
| with st.spinner("π Processing..."): | |
| try: | |
| pipeline = IngestionPipeline() | |
| result = pipeline.process_document( | |
| tmp_path, | |
| { | |
| "uploaded_at": str(datetime.now()), | |
| "filename": uploaded_file.name, | |
| "user": "demo" | |
| } | |
| ) | |
| st.session_state.ingested_count += result["chunks"] | |
| st.session_state.doc_count += 1 | |
| st.success(f"β {result['chunks']} chunks added!") | |
| st.balloons() | |
| except Exception as e: | |
| st.error(f"β Error: {str(e)}") | |
| try: | |
| os.unlink(tmp_path) | |
| except: | |
| pass | |
| st.divider() | |
| # Analytics Dashboard | |
| st.subheader("π Analytics") | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| st.metric( | |
| "π Documents", | |
| st.session_state.doc_count, | |
| delta="+1" if st.session_state.doc_count > 0 else None | |
| ) | |
| with col2: | |
| st.metric( | |
| "π Chunks", | |
| st.session_state.ingested_count, | |
| delta=f"+{st.session_state.ingested_count}" if st.session_state.ingested_count > 0 else None | |
| ) | |
| # Confidence gauge | |
| if st.session_state.messages: | |
| last_msg = st.session_state.messages[-1] | |
| if "confidence" in last_msg: | |
| confidence = last_msg["confidence"] * 100 | |
| fig = go.Figure(go.Indicator( | |
| mode="gauge+number", | |
| value=confidence, | |
| domain={'x': [0, 1], 'y': [0, 1]}, | |
| title={'text': "Last Response Confidence"}, | |
| gauge={ | |
| 'axis': {'range': [None, 100]}, | |
| 'bar': {'color': "#667eea"}, | |
| 'steps': [ | |
| {'range': [0, 50], 'color': "lightgray"}, | |
| {'range': [50, 80], 'color': "gray"}, | |
| {'range': [80, 100], 'color': "darkgray"} | |
| ] | |
| } | |
| )) | |
| fig.update_layout(height=200, margin=dict(l=20, r=20, t=40, b=20)) | |
| st.plotly_chart(fig, use_container_width=True) | |
| st.divider() | |
| # Quick actions | |
| st.subheader("β‘ Quick Actions") | |
| if st.button("ποΈ Clear Chat", width="stretch"): | |
| st.session_state.messages = [] | |
| st.rerun() | |
| if st.button("π§Ή Reset Database", width="stretch"): | |
| from app.vector_store.chroma_client import ChromaClient | |
| ChromaClient().delete_all() | |
| st.session_state.ingested_count = 0 | |
| st.session_state.doc_count = 0 | |
| st.success("Database cleared!") | |
| st.rerun() | |
| st.divider() | |
| st.caption("β‘ Powered by Groq + LangGraph + ChromaDB") | |
| # Main chat interface | |
| st.markdown('<div class="main-header">π§ Ask Your Documents</div>', unsafe_allow_html=True) | |
| st.markdown('<div class="sub-header">Intelligent Q&A, Summarization & Data Extraction</div>', unsafe_allow_html=True) | |
| # Display chat history | |
| for msg in st.session_state.messages: | |
| with st.chat_message(msg["role"]): | |
| st.markdown(msg["content"]) | |
| # Show sources for assistant messages | |
| if msg["role"] == "assistant" and "sources" in msg and msg["sources"]: | |
| with st.expander("π View Sources", expanded=False): | |
| for idx, src in enumerate(msg["sources"][:3]): | |
| st.markdown(f"**Source {idx+1}** (Score: {src.get('score', 0):.2f})") | |
| st.markdown(f'<div class="source-box">{src["document"][:300]}...</div>', unsafe_allow_html=True) | |
| # Show confidence | |
| if "confidence" in msg: | |
| conf = msg["confidence"] * 100 | |
| st.caption(f"π― Confidence: {conf:.0f}% {'β High' if conf > 70 else 'β οΈ Medium' if conf > 40 else 'β Low'}") | |
| # Input | |
| if prompt := st.chat_input("Ask about your documents..."): | |
| # Add user message | |
| st.session_state.messages.append({"role": "user", "content": prompt}) | |
| with st.chat_message("user"): | |
| st.markdown(prompt) | |
| # Generate response | |
| with st.chat_message("assistant"): | |
| with st.spinner("π§ Analyzing documents..."): | |
| try: | |
| # Process with agent | |
| result = st.session_state.agent.process_query(prompt) | |
| response = result.get("response", "I couldn't find an answer.") | |
| confidence = result.get("confidence", 0.0) | |
| sources = result.get("sources", []) | |
| intent = result.get("intent", "unknown") | |
| st.markdown(response) | |
| # Show sources | |
| if sources: | |
| with st.expander("π View Sources", expanded=False): | |
| for idx, src in enumerate(sources[:3]): | |
| st.markdown(f"**Source {idx+1}** (Score: {src.get('score', 0):.2f})") | |
| doc_text = src.get("document", "")[:300] | |
| st.markdown(f'<div class="source-box">{doc_text}...</div>', unsafe_allow_html=True) | |
| # Show metadata | |
| col1, col2, col3 = st.columns(3) | |
| with col1: | |
| st.caption(f"π― Confidence: {confidence*100:.0f}%") | |
| with col2: | |
| st.caption(f"π Intent: {intent}") | |
| with col3: | |
| if sources: | |
| st.caption(f"π Sources: {len(sources[:3])}") | |
| # Store in history | |
| st.session_state.messages.append({ | |
| "role": "assistant", | |
| "content": response, | |
| "sources": sources[:3], | |
| "confidence": confidence, | |
| "intent": intent | |
| }) | |
| except Exception as e: | |
| st.error(f"β Error: {str(e)}") | |
| st.session_state.messages.append({ | |
| "role": "assistant", | |
| "content": f"Sorry, I encountered an error: {str(e)}", | |
| "confidence": 0, | |
| "sources": [] | |
| }) |