import streamlit as st
# Page config MUST be the first Streamlit command
st.set_page_config(
page_title="FinSolve AI Assistant - Complete Tech Stack",
page_icon="๐ค",
layout="wide",
initial_sidebar_state="expanded"
)
import pandas as pd
import os
import sys
from typing import Dict, List, Optional
from datetime import datetime
import json
import time
# Add the src directory to Python path for imports
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
# Import our enhanced modules
from enhanced_rag_system import EnhancedRAGSystem
from auth_system import AuthSystem
from document_processor import DocumentProcessor
# Check tech stack availability with FastAPI detection
def check_tech_stack():
"""Check which tech stack components are available"""
tech_status = {}
# Python (always available)
tech_status['python'] = {'available': True, 'status': 'Core system'}
# Streamlit (always available)
tech_status['streamlit'] = {'available': True, 'status': 'UI active'}
# Vector Store (ChromaDB)
try:
import chromadb
from sentence_transformers import SentenceTransformer
tech_status['vector_store'] = {'available': True, 'status': 'ChromaDB ready'}
except ImportError:
tech_status['vector_store'] = {'available': False, 'status': 'Fallback mode'}
# LLM (OpenAI)
openai_key = os.getenv("OPENAI_API_KEY")
if openai_key:
try:
import openai
tech_status['llm'] = {'available': True, 'status': 'OpenAI GPT'}
except ImportError:
tech_status['llm'] = {'available': False, 'status': 'Template mode'}
else:
tech_status['llm'] = {'available': False, 'status': 'No API key'}
# FastAPI (check if real FastAPI is available)
try:
import requests
# Try to ping FastAPI server
response = requests.get("http://localhost:8000/health", timeout=2)
if response.status_code == 200:
tech_status['fastapi'] = {'available': True, 'status': 'Real FastAPI'}
else:
tech_status['fastapi'] = {'available': True, 'status': 'Simulated API'}
except:
try:
# Check if FastAPI is installable
import fastapi
import uvicorn
tech_status['fastapi'] = {'available': True, 'status': 'Available (not running)'}
except ImportError:
tech_status['fastapi'] = {'available': True, 'status': 'Simulated API'}
return tech_status
# Enhanced CSS with small popup tech stack indicators
st.markdown("""
""", unsafe_allow_html=True)
def display_tech_stack_notification():
"""Display tech stack status as a Streamlit notification"""
# Initialize popup state
if 'show_tech_notification' not in st.session_state:
st.session_state.show_tech_notification = True
st.session_state.notification_start_time = time.time()
# Auto-hide notification after 5 seconds
if st.session_state.show_tech_notification:
current_time = time.time()
if current_time - st.session_state.notification_start_time > 5:
st.session_state.show_tech_notification = False
st.rerun()
# Show notification if enabled
if st.session_state.show_tech_notification:
tech_status = check_tech_stack()
# Create a container for the notification
with st.container():
st.info("๐๏ธ **Tech Stack Status** - Auto-hiding in 5 seconds...")
# Create columns for status items
cols = st.columns(5)
component_names = {
'python': 'Python',
'streamlit': 'Streamlit',
'vector_store': 'Vector Store',
'llm': 'LLM',
'fastapi': 'FastAPI'
}
for i, (component, info) in enumerate(tech_status.items()):
with cols[i]:
if info['available']:
if component == 'fastapi':
icon = "๐"
color = "orange"
else:
icon = "โ
"
color = "green"
else:
icon = "โ ๏ธ"
color = "red"
st.markdown(f"""
{icon}
{component_names.get(component, component)}
{info['status']}
""", unsafe_allow_html=True)
def display_tech_status_sidebar():
"""Display tech status in sidebar"""
with st.sidebar:
st.markdown("---")
st.markdown("### ๐ง Tech Stack")
tech_status = check_tech_stack()
for component, info in tech_status.items():
if info['available']:
if component == 'fastapi':
icon = "๐"
else:
icon = "โ
"
else:
icon = "โ ๏ธ"
component_names = {
'python': 'Python',
'streamlit': 'Streamlit',
'vector_store': 'Vector Store',
'llm': 'LLM',
'fastapi': 'FastAPI'
}
st.markdown(f"{icon} **{component_names.get(component, component)}**: {info['status']}")
def initialize_session_state():
"""Initialize session state variables"""
if 'authenticated' not in st.session_state:
st.session_state.authenticated = False
if 'user_role' not in st.session_state:
st.session_state.user_role = None
if 'username' not in st.session_state:
st.session_state.username = None
if 'chat_history' not in st.session_state:
st.session_state.chat_history = []
if 'enhanced_rag_system' not in st.session_state:
st.session_state.enhanced_rag_system = None
if 'demo_mode' not in st.session_state:
st.session_state.demo_mode = False
if 'demo_role' not in st.session_state:
st.session_state.demo_role = None
def demo_role_switch():
"""Demo mode for role switching"""
st.markdown('', unsafe_allow_html=True)
st.markdown("๐งช **Demo Mode**: Switch between roles to test different access levels")
demo_roles = ["Finance", "Marketing", "HR", "Engineering", "C-Level", "Employee"]
selected_role = st.selectbox(
"Simulate User Role:",
demo_roles,
index=demo_roles.index(st.session_state.user_role) if st.session_state.user_role in demo_roles else 0,
key="demo_role_selector"
)
col1, col2 = st.columns([1, 1])
with col1:
if st.button("๐ Switch Role", key="switch_role"):
st.session_state.demo_role = selected_role
st.session_state.demo_mode = True
st.success(f"Switched to {selected_role} role for demo purposes")
with col2:
if st.button("๐ Use Actual Role", key="actual_role"):
st.session_state.demo_mode = False
st.session_state.demo_role = None
st.info(f"Using your actual role: {st.session_state.user_role}")
st.markdown('
', unsafe_allow_html=True)
def login_page():
"""Display enhanced login page with tech stack notification"""
st.markdown('๐ค FinSolve AI Assistant
Complete Tech Stack Implementation
', unsafe_allow_html=True)
# Display tech stack notification
display_tech_stack_notification()
col1, col2, col3 = st.columns([1, 2, 1])
with col2:
st.subheader("๐ Secure Login")
with st.form("login_form"):
username = st.text_input("Username", placeholder="Enter your username")
password = st.text_input("Password", type="password", placeholder="Enter your password")
submit_button = st.form_submit_button("๐ Login", use_container_width=True)
if submit_button:
auth_system = AuthSystem()
if auth_system.authenticate(username, password):
st.session_state.authenticated = True
st.session_state.username = username
st.session_state.user_role = auth_system.get_user_role(username)
# Reset notification for next page
st.session_state.show_tech_notification = True
st.session_state.notification_start_time = time.time()
st.success(f"Welcome {username}! Your role: {st.session_state.user_role}")
st.rerun()
else:
st.error("โ Invalid credentials. Please try again.")
# Enhanced demo credentials info
with st.expander("๐ Demo Credentials & Full Tech Stack"):
st.markdown("""
**Test Accounts:**
- **Finance**: `tony.finance` / `password123`
- **Marketing**: `sarah.marketing` / `password123`
- **HR**: `mike.hr` / `password123`
- **Engineering**: `peter.engineering` / `password123`
- **C-Level**: `ceo.admin` / `password123`
- **Employee**: `john.employee` / `password123`
**๐๏ธ Complete Tech Stack Implementation:**
โ
**1. Python**: Core programming language
โ
**2. FastAPI**: Simulated REST API endpoints
โ
**3. LLM**: OpenAI GPT integration + template fallback
โ
**4. Vector Store**: ChromaDB + Sentence Transformers
โ
**5. Streamlit**: Enhanced UI with rich features
**๐ง Additional Features:**
- ๐ก๏ธ RBAC enforcement at retrieval level
- ๐ Interactive visualizations (Plotly)
- ๐ Complete source attribution
- ๐ซ Graceful unauthorized access handling
- ๐ User feedback collection system
- ๐ Demo role switching for presentations
- ๐ Real-time system metrics
**๐ก Environment Setup:**
- Set `OPENAI_API_KEY` for full LLM features
- ChromaDB auto-initializes on first run
- All data embedded for zero-setup deployment
""")
def display_feedback_widget(query: str, response: str):
"""Display feedback widget for user ratings"""
st.markdown('', unsafe_allow_html=True)
st.markdown("**๐ How helpful was this response?**")
# Create a unique identifier for this specific feedback widget
# Using hash of query + response to ensure uniqueness per message
import hashlib
unique_id = hashlib.md5((query + response).encode()).hexdigest()[:8]
col1, col2, col3, col4, col5 = st.columns(5)
rating = None
with col1:
if st.button("โญ", key=f"rating_1_{unique_id}"):
rating = 1
with col2:
if st.button("โญโญ", key=f"rating_2_{unique_id}"):
rating = 2
with col3:
if st.button("โญโญโญ", key=f"rating_3_{unique_id}"):
rating = 3
with col4:
if st.button("โญโญโญโญ", key=f"rating_4_{unique_id}"):
rating = 4
with col5:
if st.button("โญโญโญโญโญ", key=f"rating_5_{unique_id}"):
rating = 5
if rating:
current_role = st.session_state.demo_role if st.session_state.demo_mode else st.session_state.user_role
st.session_state.enhanced_rag_system.store_feedback(query, response, rating, current_role)
st.success(f"Thank you for rating this response {rating}/5 stars! ๐")
st.markdown('
', unsafe_allow_html=True)
def display_system_metrics():
"""Display comprehensive system metrics"""
if st.session_state.enhanced_rag_system:
status = st.session_state.enhanced_rag_system.get_system_status()
st.markdown('', unsafe_allow_html=True)
st.markdown("**๐ System Metrics**")
# Basic metrics
col1, col2, col3 = st.columns(3)
with col1:
st.metric("Documents", status.get('documents_loaded', 0))
with col2:
st.metric("Feedback", status.get('feedback_entries', 0))
with col3:
system_health = "๐ข Healthy" if status.get('system_initialized') else "๐ด Error"
st.markdown(f"**Status:** {system_health}")
st.markdown('
', unsafe_allow_html=True)
def main_app():
"""Enhanced main application interface"""
# Display tech stack notification on app entry
display_tech_stack_notification()
# Initialize Enhanced RAG system if not done
if st.session_state.enhanced_rag_system is None:
with st.spinner("๐ Initializing Complete RAG System..."):
st.session_state.enhanced_rag_system = EnhancedRAGSystem()
st.session_state.enhanced_rag_system.initialize_system()
# Header without tech stack status (now in notification)
col1, col2, col3 = st.columns([3, 2, 1])
with col1:
st.markdown('๐ค FinSolve AI Assistant
Complete Tech Stack RAG System
', unsafe_allow_html=True)
with col2:
current_role = st.session_state.demo_role if st.session_state.demo_mode else st.session_state.user_role
demo_indicator = " (Demo)" if st.session_state.demo_mode else ""
st.markdown(f"""
๐ {st.session_state.username}
{current_role}{demo_indicator}
""", unsafe_allow_html=True)
with col3:
if st.button("๐ช Logout", use_container_width=True):
for key in list(st.session_state.keys()):
del st.session_state[key]
st.rerun()
# Demo role switching section
if st.checkbox("๐งช Enable Demo Mode", help="Switch between roles to test different access levels"):
demo_role_switch()
# Sidebar with enhanced role info and system metrics
with st.sidebar:
st.header("๐ Your Access Level")
current_role = st.session_state.demo_role if st.session_state.demo_mode else st.session_state.user_role
role_permissions = {
"Finance": ["๐ Financial reports", "๐ฐ Marketing expenses", "๐ง Equipment costs", "๐ณ Reimbursements"],
"Marketing": ["๐ Campaign performance", "๐ฌ Customer feedback", "๐ Sales metrics", "๐ฏ ROI data"],
"HR": ["๐ฅ Employee data", "๐
Attendance records", "๐ฐ Payroll info", "โญ Performance reviews"],
"Engineering": ["๐๏ธ Technical architecture", "โ๏ธ Development processes", "๐ Operational guidelines", "๐ Security docs"],
"C-Level": ["๐ Full access to all company data", "๐ Executive dashboards", "๐ Strategic metrics"],
"Employee": ["๐ General company information", "๐ Policies", "๐ Events", "โ FAQs"]
}
permissions = role_permissions.get(current_role, [])
for perm in permissions:
st.markdown(f"โ
{perm}")
st.markdown("---")
st.header("๐ก Sample Questions")
sample_questions = {
"Finance": [
"What was our Q4 2024 revenue?",
"Show me cost breakdown with charts",
"What's our marketing ROI?",
"Create financial metrics table"
],
"Marketing": [
"How did our Q4 campaigns perform?",
"Show customer acquisition trends",
"What's our digital marketing ROI?",
"Create campaign performance chart"
],
"HR": [
"What are the leave policies?",
"Show me employee benefits",
"What's our compensation structure?",
"How do I apply for maternity leave?"
],
"Engineering": [
"What's our system architecture?",
"Show me our technology stack",
"What's our deployment process?",
"Explain our security measures"
],
"C-Level": [
"Give me a company overview",
"Show all department metrics",
"What are our growth trends?",
"Create executive dashboard"
],
"Employee": [
"What are the company policies?",
"How do I apply for leave?",
"What benefits do we have?",
"What's the dress code?"
]
}
questions = sample_questions.get(current_role, [])
for q in questions:
if st.button(q, key=f"sample_{q}", use_container_width=True):
st.session_state.current_query = q
# System metrics for all users
st.markdown("---")
display_system_metrics()
# Tech stack status in sidebar
display_tech_status_sidebar()
# Main chat interface
st.header("๐ฌ AI-Powered Chat with Complete RAG")
# Display chat history with enhanced styling
for i, chat_item in enumerate(st.session_state.chat_history):
query, response, sources, visualization, table = chat_item
# User message
st.markdown(f"""
๐ค You: {query}
""", unsafe_allow_html=True)
# Assistant response
response_class = "unauthorized-message" if "Access Restricted" in response else "assistant-message"
st.markdown(f"""
๐ค AI Assistant:
{response}
""", unsafe_allow_html=True)
# Display visualization if available
if visualization:
st.markdown("๐ **Interactive Data Visualization:**")
st.components.v1.html(visualization, height=450)
# Display table if available
if table:
st.markdown("๐ **Data Table:**")
st.markdown(table, unsafe_allow_html=True)
# Display sources
if sources:
st.markdown(f"""
๐ Sources: {" | ".join(sources)}
""", unsafe_allow_html=True)
# Feedback widget
if "Access Restricted" not in response:
display_feedback_widget(query, response)
st.markdown("---")
# Query input with enhanced features
query = st.text_input(
"๐ Ask your question:",
value=st.session_state.get('current_query', ''),
placeholder="Type your question here... (e.g., 'Show me Q4 revenue with charts')",
key="query_input"
)
col1, col2, col3 = st.columns([1, 1, 3])
with col1:
ask_button = st.button("๐ Ask AI", use_container_width=True)
with col2:
clear_button = st.button("๐๏ธ Clear Chat", use_container_width=True)
if clear_button:
st.session_state.chat_history = []
st.rerun()
if ask_button and query:
with st.spinner("๐ง Processing with complete RAG pipeline..."):
try:
current_role = st.session_state.demo_role if st.session_state.demo_mode else st.session_state.user_role
response, sources, visualization, table = st.session_state.enhanced_rag_system.query(
query,
current_role
)
# Add to chat history
st.session_state.chat_history.append((query, response, sources, visualization, table))
# Clear the current query
if 'current_query' in st.session_state:
del st.session_state.current_query
st.rerun()
except Exception as e:
st.error(f"โ Error processing query: {str(e)}")
def main():
"""Main application entry point"""
initialize_session_state()
if not st.session_state.authenticated:
login_page()
else:
main_app()
if __name__ == "__main__":
main()