""" LifeUnity AI β€” Cognitive Twin System Main Streamlit Application with 4-page interface """ import streamlit as st import cv2 import numpy as np from PIL import Image import plotly.graph_objects as go import plotly.express as px from datetime import datetime, timedelta import pandas as pd # Import modules from app.mood_detection import predict_emotion from app.memory_graph import get_memory_graph from app.user_profile import get_user_profile from app.insights_engine import get_insights_engine from app.utils.logger import get_logger # Initialize logger logger = get_logger("MainApp") # Page configuration st.set_page_config( page_title="LifeUnity AI - Cognitive Twin", page_icon="🧠", layout="wide", initial_sidebar_state="expanded" ) # Custom CSS st.markdown(""" """, unsafe_allow_html=True) # Initialize session state if 'user_profile' not in st.session_state: st.session_state.user_profile = get_user_profile() if 'mood_detector' not in st.session_state: st.session_state.mood_detector = get_mood_detector() if 'memory_graph' not in st.session_state: st.session_state.memory_graph = get_memory_graph() if 'insights_engine' not in st.session_state: st.session_state.insights_engine = get_insights_engine() def render_sidebar(): """Render the sidebar navigation.""" with st.sidebar: st.markdown("# 🧠 LifeUnity AI") st.markdown("### Cognitive Twin System") st.markdown("---") # User info profile_summary = st.session_state.user_profile.get_summary() st.markdown(f"**User:** {profile_summary['user_id']}") st.markdown(f"**Tracked Emotions:** {profile_summary['total_emotions_tracked']}") st.markdown(f"**Memory Notes:** {profile_summary['total_notes']}") st.markdown("---") # Navigation page = st.radio( "Navigate to:", ["πŸ“Š Dashboard", "😊 Mood Detection", "🧩 Cognitive Memory", "πŸ’‘ AI Insights"], label_visibility="collapsed" ) st.markdown("---") st.markdown("### Quick Stats") st.metric("Stress Level", f"{profile_summary['current_stress_level']:.0f}/100") st.metric("Productivity", f"{profile_summary['current_productivity']:.0f}/100") return page def render_dashboard(): """Render the Dashboard page.""" st.markdown('
πŸ“Š Dashboard
', unsafe_allow_html=True) st.markdown("### Welcome to Your Cognitive Twin System") # Get data profile = st.session_state.user_profile profile_summary = profile.get_summary() emotion_history = profile.get_emotion_history(limit=10) insights = st.session_state.insights_engine memory_stats = st.session_state.memory_graph.get_graph_stats() # Top metrics row col1, col2, col3, col4 = st.columns(4) with col1: stress_level = profile_summary['current_stress_level'] stress_color = "🟒" if stress_level < 40 else "🟑" if stress_level < 70 else "πŸ”΄" st.metric( label="Current Stress", value=f"{stress_level:.0f}/100", delta=f"{stress_color}", delta_color="off" ) with col2: productivity = profile_summary['current_productivity'] prod_color = "🟒" if productivity >= 70 else "🟑" if productivity >= 50 else "πŸ”΄" st.metric( label="Productivity Score", value=f"{productivity:.0f}/100", delta=f"{prod_color}", delta_color="off" ) with col3: st.metric( label="Tracked Emotions", value=profile_summary['total_emotions_tracked'] ) with col4: st.metric( label="Memory Nodes", value=memory_stats['total_memories'] ) st.markdown("---") # Two column layout col_left, col_right = st.columns([3, 2]) with col_left: st.markdown("### πŸ“ˆ Recent Mood Trend") if emotion_history: # Prepare data for chart emotions_df = pd.DataFrame(emotion_history) emotions_df['timestamp'] = pd.to_datetime(emotions_df['timestamp']) # Create emotion mapping for numeric representation emotion_map = { 'happy': 5, 'surprise': 4, 'neutral': 3, 'sad': 2, 'angry': 1, 'fear': 1, 'disgust': 1 } emotions_df['emotion_value'] = emotions_df['emotion'].map(emotion_map) # Plot fig = px.line( emotions_df, x='timestamp', y='emotion_value', markers=True, title="Emotion Timeline (Higher = More Positive)" ) fig.update_layout(height=300) st.plotly_chart(fig, use_container_width=True) # Emotion distribution st.markdown("### 😊 Emotion Distribution") emotion_counts = emotions_df['emotion'].value_counts() fig_pie = px.pie( values=emotion_counts.values, names=emotion_counts.index, title="Recent Emotion Breakdown" ) fig_pie.update_layout(height=300) st.plotly_chart(fig_pie, use_container_width=True) else: st.info("No emotion data yet. Visit the Mood Detection page to get started!") with col_right: st.markdown("### 🧩 Memory Graph Preview") st.metric("Total Memories", memory_stats['total_memories']) st.metric("Connections", memory_stats['total_connections']) st.metric("Memory Clusters", memory_stats['num_clusters']) if memory_stats['total_memories'] > 0: avg_connections = memory_stats['avg_connections'] st.metric("Avg Connections", f"{avg_connections:.1f}") st.markdown("### πŸ“ Recent Memories") memories = st.session_state.memory_graph.get_all_memories() recent = sorted(memories, key=lambda x: x['timestamp'], reverse=True)[:3] for mem in recent: with st.expander(f"Memory #{mem['id']}"): st.write(mem['content'][:150] + "..." if len(mem['content']) > 150 else mem['content']) st.caption(f"πŸ“… {mem['timestamp'][:10]}") else: st.info("No memories yet. Add some in the Cognitive Memory page!") def render_mood_detection(): """Render the Mood Detection page.""" st.markdown('
😊 Mood Detection
', unsafe_allow_html=True) st.markdown("### Emotion Detection via Image Upload") st.info("ℹ️ Upload a clear photo of your face for emotion analysis") detector = st.session_state.mood_detector profile = st.session_state.user_profile st.markdown("---") # Image upload only (HuggingFace Spaces compatible) uploaded_file = st.file_uploader( "Upload an image of your face", type=['jpg', 'jpeg', 'png'], help="Upload a clear photo showing your face for emotion detection" ) if uploaded_file is not None: # Load and display image image = Image.open(uploaded_file) image_np = np.array(image) col1, col2 = st.columns(2) with col1: st.image(image, caption="Uploaded Image", use_column_width=True) with col2: with st.spinner("Analyzing emotion..."): result = detector.detect_emotion(image_np, return_all=True) if result['face_detected']: emotion = result['emotion'] confidence = result['confidence'] # Display result st.markdown(f"### Detected Emotion: {emotion.title()} {detector.get_emotion_emoji(emotion)}") st.markdown(f"**Confidence:** {confidence*100:.1f}%") # Progress bar for confidence st.progress(confidence) # Save to profile if st.button("πŸ’Ύ Save to Profile", key="save_emotion"): profile.add_emotion_record(emotion, confidence) st.success("βœ… Emotion saved to your profile!") # Show all emotions if result.get('all_emotions'): st.markdown("### All Detected Emotions") emotions_data = result['all_emotions'] for emo, score in sorted(emotions_data.items(), key=lambda x: x[1], reverse=True): st.write(f"{emo.title()}: {score*100:.1f}%") else: st.error("❌ No face detected in the image. Please upload a clearer photo with a visible face.") # Recent emotion history st.markdown("---") st.markdown("### πŸ“Š Recent Emotion History") emotion_history = profile.get_emotion_history(limit=5) if emotion_history: for record in reversed(emotion_history): col1, col2, col3 = st.columns([2, 2, 3]) with col1: st.write(f"{detector.get_emotion_emoji(record['emotion'])} **{record['emotion'].title()}**") with col2: st.write(f"Confidence: {record['confidence']*100:.1f}%") with col3: timestamp = datetime.fromisoformat(record['timestamp']) st.write(f"πŸ“… {timestamp.strftime('%Y-%m-%d %H:%M')}") else: st.info("No emotion history yet. Detect your first emotion above!") def render_cognitive_memory(): """Render the Cognitive Memory page.""" st.markdown('
🧩 Cognitive Memory
', unsafe_allow_html=True) st.markdown("### Your Personal Knowledge Graph") memory_graph = st.session_state.memory_graph # Add new memory st.markdown("### βž• Add New Memory") with st.form("add_memory_form"): note_content = st.text_area( "Write your note or memory:", height=100, placeholder="Enter your thoughts, ideas, or experiences..." ) tags_input = st.text_input( "Tags (comma-separated):", placeholder="work, personal, idea, etc." ) submitted = st.form_submit_button("πŸ’Ύ Save Memory") if submitted and note_content: tags = [tag.strip() for tag in tags_input.split(',')] if tags_input else [] with st.spinner("Processing and embedding memory..."): memory_id = memory_graph.add_memory(note_content, tags=tags) if memory_id > 0: st.success(f"βœ… Memory saved! (ID: {memory_id})") # Also add to user profile st.session_state.user_profile.add_note(note_content, tags=tags) else: st.error("❌ Failed to save memory. Please try again.") st.markdown("---") # Memory statistics col1, col2 = st.columns(2) with col1: st.markdown("### πŸ“Š Memory Statistics") stats = memory_graph.get_graph_stats() st.metric("Total Memories", stats['total_memories']) st.metric("Total Connections", stats['total_connections']) st.metric("Memory Clusters", stats['num_clusters']) if stats['total_memories'] > 0: st.metric("Avg Connections", f"{stats['avg_connections']:.2f}") with col2: st.markdown("### πŸ” Search Memories") search_query = st.text_input( "Search your memories:", placeholder="What are you looking for?" ) if search_query: with st.spinner("Searching..."): results = memory_graph.search_memories(search_query, top_k=5) if results: st.markdown(f"**Found {len(results)} relevant memories:**") for result in results: with st.expander(f"Memory #{result['id']} - Similarity: {result['similarity']*100:.1f}%"): st.write(result['content']) st.caption(f"πŸ“… {result['timestamp'][:10]}") if result['tags']: st.write(f"🏷️ Tags: {', '.join(result['tags'])}") else: st.info("No matching memories found.") # Display all memories st.markdown("---") st.markdown("### πŸ“š All Memories") memories = memory_graph.get_all_memories() if memories: # Sort by timestamp (newest first) sorted_memories = sorted(memories, key=lambda x: x['timestamp'], reverse=True) for memory in sorted_memories: with st.expander(f"Memory #{memory['id']} - {memory['timestamp'][:10]}"): st.write(memory['content']) if memory.get('tags'): st.write(f"🏷️ Tags: {', '.join(memory['tags'])}") # Show related memories related = memory_graph.get_related_memories(memory['id']) if related: st.write(f"πŸ”— Connected to {len(related)} other memories") # Delete button if st.button(f"πŸ—‘οΈ Delete", key=f"del_{memory['id']}"): if memory_graph.delete_memory(memory['id']): st.success("Memory deleted!") st.rerun() else: st.info("No memories yet. Add your first memory above!") # Memory graph visualization if memories and len(memories) > 1: st.markdown("---") st.markdown("### πŸ•ΈοΈ Memory Graph Visualization") clusters = memory_graph.get_memory_clusters() st.write(f"Your memories form {len(clusters)} clusters of related thoughts.") for idx, cluster in enumerate(clusters): st.write(f"**Cluster {idx + 1}:** {len(cluster)} memories") def render_ai_insights(): """Render the AI Insights page.""" st.markdown('
πŸ’‘ AI Insights
', unsafe_allow_html=True) st.markdown("### Proactive Well-being Intelligence") insights_engine = st.session_state.insights_engine # Generate daily report button if st.button("πŸ”„ Generate Daily Report", type="primary"): with st.spinner("Analyzing your data and generating insights..."): report = insights_engine.generate_daily_report() st.session_state.daily_report = report # Display report if available if 'daily_report' in st.session_state: report = st.session_state.daily_report # Header st.markdown(f"## πŸ“‹ Daily Report - {report['date']}") st.caption(f"Generated at: {report['generated_at'][:19]}") st.markdown("---") # Metrics row col1, col2, col3 = st.columns(3) metrics = report['metrics'] with col1: stress = metrics['stress_level'] stress_color = "🟒" if stress < 40 else "🟑" if stress < 70 else "πŸ”΄" st.metric("Stress Level", f"{stress:.0f}/100", delta=stress_color, delta_color="off") with col2: productivity = metrics['productivity_score'] prod_color = "🟒" if productivity >= 70 else "🟑" if productivity >= 50 else "πŸ”΄" st.metric("Productivity", f"{productivity:.0f}/100", delta=prod_color, delta_color="off") with col3: fatigue = metrics['fatigue_risk'] fatigue_emoji = "🟒" if fatigue == "low" else "🟑" if fatigue == "moderate" else "πŸ”΄" st.metric("Fatigue Risk", fatigue.title(), delta=fatigue_emoji, delta_color="off") # Alerts if report['alerts']: st.markdown("---") st.markdown("### ⚠️ Alerts") for alert in report['alerts']: st.markdown(f'
🚨 {alert["message"]}
', unsafe_allow_html=True) # Insights st.markdown("---") st.markdown("### 🧠 Insights") col1, col2 = st.columns(2) with col1: st.markdown("#### Stress Analysis") stress_insight = report['insights']['stress'] st.markdown(f'
', unsafe_allow_html=True) st.markdown(f"**Status:** {stress_insight['status']}") st.write(stress_insight['description']) st.markdown('
', unsafe_allow_html=True) with col2: st.markdown("#### Productivity Analysis") prod_insight = report['insights']['productivity'] st.markdown(f'
', unsafe_allow_html=True) st.markdown(f"**Status:** {prod_insight['status']}") st.write(prod_insight['description']) st.markdown('
', unsafe_allow_html=True) # Recommendations st.markdown("---") st.markdown("### πŸ’‘ Recommendations") recommendations = report['recommendations'] if recommendations: for rec in recommendations: priority_emoji = "πŸ”΄" if rec['priority'] == 'high' else "🟑" if rec['priority'] == 'medium' else "🟒" st.markdown(f'
', unsafe_allow_html=True) st.markdown(f"### {priority_emoji} {rec['category']}") st.write(f"**Suggestion:** {rec['suggestion']}") st.write(f"**Action:** {rec['action']}") st.markdown('
', unsafe_allow_html=True) else: st.info("No specific recommendations at this time. Keep up the good work!") else: st.info("πŸ‘† Click 'Generate Daily Report' above to get your personalized AI insights!") # Emotion Pattern Analysis st.markdown("---") st.markdown("### πŸ“Š Emotion Pattern Analysis") days_to_analyze = st.slider("Analyze last N days:", 1, 30, 7) if st.button("Analyze Patterns"): with st.spinner("Analyzing emotion patterns..."): patterns = insights_engine.analyze_emotion_patterns(days=days_to_analyze) st.markdown(f"### Analysis for Last {patterns.get('period_days', days_to_analyze)} Days") col1, col2 = st.columns(2) with col1: st.metric("Total Records", patterns.get('total_records', 0)) st.write(f"**Overall Trend:** {patterns.get('trend', 'neutral').title()}") if patterns.get('dominant_emotions'): st.write("**Dominant Emotions:**") for emotion in patterns['dominant_emotions']: st.write(f"- {emotion.title()}") with col2: if patterns.get('emotion_distribution'): st.write("**Emotion Distribution:**") dist = patterns['emotion_distribution'] for emotion, count in sorted(dist.items(), key=lambda x: x[1], reverse=True): st.write(f"{emotion.title()}: {count}") # Memory Insights st.markdown("---") st.markdown("### 🧩 Memory Insights") memory_insights = insights_engine.suggest_memory_insights(limit=5) if memory_insights: st.write("Recent memories with their relationship network:") for insight in memory_insights: with st.expander(f"Memory #{insight['memory_id']} - {insight['related_count']} connections"): st.write(insight['content_preview']) st.caption(f"πŸ“… {insight['timestamp'][:10]}") if insight['tags']: st.write(f"🏷️ Tags: {', '.join(insight['tags'])}") else: st.info("No memory insights available. Add some memories in the Cognitive Memory page!") def main(): """Main application entry point.""" try: # Render sidebar and get selected page page = render_sidebar() # Route to appropriate page if page == "πŸ“Š Dashboard": render_dashboard() elif page == "😊 Mood Detection": render_mood_detection() elif page == "🧩 Cognitive Memory": render_cognitive_memory() elif page == "πŸ’‘ AI Insights": render_ai_insights() except Exception as e: logger.error(f"Application error: {str(e)}", exc_info=True) st.error("An error occurred. Please refresh the page or contact support.") st.exception(e) if __name__ == "__main__": main()