""" Page Manager for Pharmaceutical Analytics Application This module manages the different pages/views of the application, enabling a multi-page structure without bloating the main app.py. """ import streamlit as st import pandas as pd import plotly.express as px import plotly.graph_objects as go from datetime import datetime import time import threading import queue import sys import traceback import json # Import diagnostics module from updated_diagnostics import render_diagnostics_tab def run_workflow_in_thread(workflow, alert): """Run the workflow in a separate thread and update session state""" try: # Update status st.session_state.status_queue.put(("info", "Planning analysis approach...")) st.session_state.current_step = "planning" # Run the workflow print(f"Starting workflow with alert: {alert}") result = workflow.run_workflow(alert) print("Workflow completed successfully") # Store the result st.session_state.workflow_state = result # Update status st.session_state.status_queue.put(("success", "Analysis complete!")) st.session_state.current_step = "complete" except Exception as e: error_details = f"Error in workflow: {str(e)}\n{traceback.format_exc()}" print(error_details) st.session_state.status_queue.put(("error", f"Error: {str(e)}")) st.session_state.current_step = "error" # Store error details if "error_details" not in st.session_state: st.session_state.error_details = [] st.session_state.error_details.append(error_details) def run_workflow_directly(alert_text): """Run the workflow directly (not in a thread) for debugging""" try: # Create the workflow SalesAnalysisWorkflow = st.session_state.agents["SalesAnalysisWorkflow"] workflow = SalesAnalysisWorkflow(db_path="data/pharma_db.sqlite") # Update status st.session_state.current_step = "planning" # Run the workflow with verbose debug output st.info("Running workflow directly...") # Add detailed debugging information at each step st.write("1. Creating workflow instance") # Store the alert in session state st.session_state.alert = alert_text # Run the workflow step by step st.write("2. Starting workflow execution") result = workflow.run_workflow(alert_text) # Print workflow state st.write("3. Workflow execution completed") st.write(f"Status: {result.get('status', 'unknown')}") st.write(f"Plan created: {'Yes' if result.get('plan') else 'No'}") st.write(f"Data sources: {len(result.get('data_sources', {}))}") st.write(f"Analysis results: {len(result.get('analysis_results', {}))}") st.write(f"Validation results: {len(result.get('validation_results', {}))}") st.write(f"Insight requests: {len(result.get('insight_requests', []))}") st.write(f"Insight cards: {len(result.get('insight_cards', {}))}") # Store the result st.session_state.workflow_state = result st.session_state.current_step = "complete" # Add debug output if "insight_requests" in result and result["insight_requests"]: st.write("Insight Requests Details:") for i, req in enumerate(result["insight_requests"]): st.write(f"Request {i+1}: ID={req.request_id}") return True except Exception as e: st.error(f"Error in direct workflow execution: {str(e)}") st.code(traceback.format_exc()) return False def render_main_page(): """Render the main page with alert input and sample dashboard""" st.header("📱 Incoming Alert") col1, col2 = st.columns([3, 1]) with col1: # Dropdown for alert selection alert_option = st.selectbox( "Select Alert Type:", [ "Sales Decline", "Market Share Loss", "Inventory Issue", "Competitor Launch", "Custom Alert" ] ) # Alert templates alert_templates = { "Sales Decline": "Sales of DrugX down 15% in Northeast region over past 30 days compared to forecast.", "Market Share Loss": "Market share of DrugX decreased by 8 percentage points in the Southeast region over the last quarter.", "Inventory Issue": "Multiple stockouts of DrugX reported in Midwest distribution centers affecting 25% of pharmacies.", "Competitor Launch": "Competitor MedCorp launched similar product at 20% lower price point in Western territories.", "Custom Alert": "" } # Alert description alert_text = st.text_area( "Alert Description:", value=alert_templates[alert_option], height=100 ) # Submit button (only enabled if agents are loaded) submit_button = st.button("Analyze Alert", disabled=not st.session_state.get("agents_loaded", False)) if submit_button and alert_text: try: # Initialize queue if not exists if "status_queue" not in st.session_state: st.session_state.status_queue = queue.Queue() # Create and start the workflow SalesAnalysisWorkflow = st.session_state.agents["SalesAnalysisWorkflow"] workflow = SalesAnalysisWorkflow(db_path="data/pharma_db.sqlite") # Show initial status st.info("Starting analysis workflow...") st.session_state.logs.append({ "timestamp": datetime.now().isoformat(), "type": "info", "message": "Starting analysis workflow..." }) # Store the alert in session state st.session_state.alert = alert_text # Start the workflow in a separate thread thread = threading.Thread( target=run_workflow_in_thread, args=(workflow, alert_text), daemon=True ) thread.start() # Store the thread and mark alert as submitted st.session_state.workflow_thread = thread st.session_state.alert_submitted = True # Change active page to analysis viewer st.session_state.active_page = "analysis" # Force page refresh st.rerun() except Exception as e: st.error(f"Error starting workflow: {str(e)}") st.code(traceback.format_exc()) # Add fallback direct execution button if alert_text and st.session_state.get("agents_loaded", False): if st.button("Debug: Run Directly (No Thread)"): if run_workflow_directly(alert_text): st.success("Workflow completed! Check the Analysis Viewer page.") st.session_state.active_page = "analysis" st.rerun() with col2: st.image("https://img.icons8.com/fluency/240/000000/notification-center.png", width=150) st.markdown("**Source:** Mobile Alert System") st.markdown("**Priority:** High") st.markdown("**Time:** " + datetime.now().strftime("%Y-%m-%d %H:%M")) # Show example dashboard st.markdown("---") st.header("📊 Example Dashboard Preview") # Create sample visualization fig = px.line( pd.DataFrame({ 'Month': pd.date_range(start='2023-01-01', periods=12, freq='M'), 'Sales': [1200, 1250, 1300, 1400, 1500, 1600, 1550, 1500, 1450, 1300, 1200, 1150], 'Target': [1200, 1250, 1300, 1350, 1400, 1450, 1500, 1550, 1600, 1650, 1700, 1750] }), x='Month', y=['Sales', 'Target'], title="DrugX Performance (Sample Data)", labels={"value": "Sales ($K)", "variable": "Metric"} ) fig.update_layout(height=400) st.plotly_chart(fig, use_container_width=True) def render_page(): """Render the current active page based on session state""" # Initialize session state if needed initialize_session() # Process status updates from thread if workflow is running if st.session_state.get("workflow_thread") and st.session_state.workflow_thread.is_alive(): while not st.session_state.status_queue.empty(): status_type, status_message = st.session_state.status_queue.get() if status_type == "info": st.info(status_message) elif status_type == "success": st.success(status_message) elif status_type == "error": st.error(status_message) # Add to logs st.session_state.logs.append({ "timestamp": datetime.now().isoformat(), "type": status_type, "message": status_message }) # Create navigation in sidebar with st.sidebar: st.header("Navigation") # Only show navigation options if environment is ready if st.session_state.get("environment_ready", False): pages = { "main": "Home", "analysis": "Analysis Viewer", "diagnostics": "Diagnostics" } # Only show Analysis Viewer option if an analysis exists if st.session_state.get("workflow_state") is None and "analysis" in pages: pages = {k: v for k, v in pages.items() if k != "analysis"} # Radio buttons for navigation selected_page = st.radio("Go to", list(pages.values())) # Map selection back to page key reverse_pages = {v: k for k, v in pages.items()} st.session_state.active_page = reverse_pages.get(selected_page, "main") # Render the active page active_page = st.session_state.get("active_page", "main") if active_page == "main": render_main_page() elif active_page == "analysis": workflow_state = st.session_state.get("workflow_state") if workflow_state: if workflow_state.get("status") == "complete": render_analysis_results(workflow_state) else: render_analysis_progress() else: st.info("No analysis is currently running. Start an analysis from the Home page.") st.session_state.active_page = "main" st.rerun() elif active_page == "diagnostics": render_diagnostics_tab() else: st.error(f"Unknown page: {active_page}") st.session_state.active_page = "main" def render_analysis_progress(): """Render the analysis progress view""" # Show progress current_step = st.session_state.current_step # Progress indicators st.markdown("### 🔄 Analysis in Progress") # Progress bar steps = ["planning", "data_collection", "analysis", "validation", "insights", "complete"] step_idx = steps.index(current_step) if current_step in steps else 0 progress = (step_idx + 1) / len(steps) progress_bar = st.progress(progress) # Step indicators col1, col2, col3, col4, col5 = st.columns(5) with col1: check = "✅" if step_idx >= 0 else "🔄" check = "🔄" if step_idx == 0 else check st.markdown(f"{check} **Planning**") with col2: check = "✅" if step_idx >= 1 else "⏳" check = "🔄" if step_idx == 1 else check st.markdown(f"{check} **Data Collection**") with col3: check = "✅" if step_idx >= 2 else "⏳" check = "🔄" if step_idx == 2 else check st.markdown(f"{check} **Analysis**") with col4: check = "✅" if step_idx >= 3 else "⏳" check = "🔄" if step_idx == 3 else check st.markdown(f"{check} **Validation**") with col5: check = "✅" if step_idx >= 4 else "⏳" check = "🔄" if step_idx == 4 else check st.markdown(f"{check} **Insights**") # Show current activity based on step st.markdown("---") # Display the alert being analyzed alert_container = st.container(border=True) with alert_container: st.markdown("**Alert Being Analyzed:**") st.info(st.session_state.get("alert", "Unknown alert")) # Show thinking animation or appropriate step visualization thinking_container = st.container() with thinking_container: if current_step == "planning": render_planning_activity() elif current_step == "data_collection": render_data_collection_activity() elif current_step == "analysis": render_analysis_activity() elif current_step == "validation": render_validation_activity() elif current_step == "insights": render_insights_activity() # Placeholder for log console with st.expander("View Process Log"): for log in st.session_state.logs: timestamp = log.get("timestamp", "") message = log.get("message", "") log_type = log.get("type", "info") if log_type == "error": st.error(f"{timestamp}: {message}") else: st.text(f"{timestamp}: {message}") # Display any error details if available if "error_details" in st.session_state and st.session_state.error_details: with st.expander("Error Details", expanded=True): for i, error in enumerate(st.session_state.error_details): st.error(f"Error {i+1}:") st.code(error) # Auto refresh only if thread is still running if st.session_state.get("workflow_thread") and st.session_state.workflow_thread.is_alive(): time.sleep(0.5) st.rerun() def render_planning_activity(): """Render Planning Agent activity visualization""" st.markdown("### 🧠 Planning Agent at Work") st.markdown("The Planning Agent is decomposing the problem and creating an analysis plan.") # Show thinking animation thinking_messages = [ "Identifying required data sources...", "Determining appropriate analytical approaches...", "Creating task dependency graph...", "Designing validation strategy..." ] thinking_placeholder = st.empty() for i in range(len(thinking_messages)): thinking_placeholder.info(thinking_messages[i % len(thinking_messages)]) time.sleep(0.5) def render_data_collection_activity(): """Render Data Agent activity visualization""" st.markdown("### 🗃️ Data Agent at Work") st.markdown("The Data Agent is translating requirements into SQL queries and collecting data.") # Show SQL generation animation if st.session_state.get("show_sql", False): sql_placeholder = st.empty() example_sql = """ -- Retrieving DrugX sales data by region SELECT r.region_name, strftime('%Y-%m', s.sale_date) as month, SUM(s.units_sold) as total_units, SUM(s.revenue) as total_revenue, SUM(s.margin) as total_margin FROM sales s JOIN regions r ON s.region_id = r.region_id WHERE s.product_id = 'DRX' AND s.sale_date >= date('now', '-90 days') GROUP BY r.region_name, strftime('%Y-%m', s.sale_date) ORDER BY r.region_name, month; """ for i in range(0, min(len(example_sql), 100), 10): sql_placeholder.code(example_sql[:i] + "▌", language="sql") time.sleep(0.02) sql_placeholder.code(example_sql, language="sql") def render_analysis_activity(): """Render Analytics Agent activity visualization""" st.markdown("### 📊 Analytics Agent at Work") st.markdown("The Analytics Agent is performing statistical analysis and modeling.") # Show Python code generation animation if st.session_state.get("show_code", False): code_placeholder = st.empty() example_code = """ import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns from statsmodels.tsa.seasonal import seasonal_decompose def analyze_sales_decline(sales_df): # Convert date column to datetime sales_df['date'] = pd.to_datetime(sales_df['date']) # Focus on Northeast region ne_sales = sales_df[sales_df['region'] == 'Northeast'] # Perform time series decomposition result = seasonal_decompose(ne_sales['sales'], model='multiplicative', period=12) # Calculate percentage decline latest_month = ne_sales['date'].max() prev_month = latest_month - pd.DateOffset(months=1) latest_sales = ne_sales[ne_sales['date'] == latest_month]['sales'].values[0] prev_sales = ne_sales[ne_sales['date'] == prev_month]['sales'].values[0] pct_decline = (prev_sales - latest_sales) / prev_sales * 100 """ for i in range(0, min(len(example_code), 200), 20): code_placeholder.code(example_code[:i] + "▌", language="python") time.sleep(0.01) code_placeholder.code(example_code, language="python") def render_validation_activity(): """Render QA Agent activity visualization""" st.markdown("### 🔍 QA Agent at Work") st.markdown("The QA Agent is validating the analysis results for accuracy and completeness.") # Show quality checks checks = [ {"check": "Data completeness", "status": "Running..."}, {"check": "Statistical significance", "status": "Waiting..."}, {"check": "Alternative hypotheses", "status": "Waiting..."}, {"check": "Insight relevance", "status": "Waiting..."} ] check_placeholder = st.empty() for i in range(min(len(checks), 4)): checks[i]["status"] = "Passed ✅" check_table = pd.DataFrame(checks) check_placeholder.table(check_table) time.sleep(0.5) def render_insights_activity(): """Render Insights Agent activity visualization""" st.markdown("### 💡 Insights Agent at Work") st.markdown("The Insights Agent is generating visualizations and actionable recommendations.") # Show visualization generation viz_placeholder = st.empty() # Create sample visualization progressively for i in range(1, 5): fig = go.Figure() months = pd.date_range(start='2023-01-01', periods=12, freq='M') sales = [1200, 1250, 1300, 1400, 1500, 1600, 1550, 1500, 1450, 1300, 1200, 1150] targets = [1200, 1250, 1300, 1350, 1400, 1450, 1500, 1550, 1600, 1650, 1700, 1750] if i >= 1: fig.add_trace(go.Scatter( x=months[:8+i], y=targets[:8+i], mode='lines+markers', name='Target', line=dict(color='green', width=2) )) if i >= 2: fig.add_trace(go.Scatter( x=months[:8+i], y=sales[:8+i], mode='lines+markers', name='Actual Sales', line=dict(color='blue', width=2) )) if i >= 3: # Add competitor launch annotation fig.add_vline( x=months[9], line_dash="dash", line_color="red", annotation_text="Competitor Launch" if i >= 4 else "" ) fig.update_layout( title="DrugX Performance Analysis", xaxis_title="Month", yaxis_title="Sales ($K)", height=400 ) viz_placeholder.plotly_chart(fig, use_container_width=True) time.sleep(0.5) def render_analysis_results(workflow_state): """Render completed analysis results""" st.markdown("### ✅ Analysis Complete") # Display debug info at the top if there were any issues if "error_details" in st.session_state and st.session_state.error_details: with st.expander("Debug Information", expanded=False): for i, error in enumerate(st.session_state.error_details): st.error(f"Error {i+1}:") st.code(error) # Extract plan and insights plan = workflow_state.get("plan") insight_cards = workflow_state.get("insight_cards", {}) visualizations = workflow_state.get("visualizations", []) # Display the alert st.markdown("#### 📱 Alert Analyzed") st.info(workflow_state.get("alert", "")) # Display the problem statement if plan: st.markdown("#### 🎯 Problem Analysis") st.markdown(plan.problem_statement) # Display insight cards st.markdown("---") st.markdown("### 💡 Key Insights") if insight_cards: for card_id, card in insight_cards.items(): # Create a card-like container with st.container(): st.subheader(card.title) st.markdown(card.description) # Display visualizations if available if hasattr(card, 'charts') and card.charts and len(visualizations) > 0: # Generate sample visualizations cols = st.columns(min(len(card.charts), 2)) for i, chart_name in enumerate(card.charts[:2]): with cols[i % 2]: # Create appropriate chart based on name if "trend" in chart_name.lower(): # Create sample time series months = pd.date_range(start='2023-01-01', periods=12, freq='M') sales = [1200, 1250, 1300, 1400, 1500, 1600, 1550, 1500, 1450, 1300, 1200, 1150] targets = [1200, 1250, 1300, 1350, 1400, 1450, 1500, 1550, 1600, 1650, 1700, 1750] fig = go.Figure() fig.add_trace(go.Scatter( x=months, y=sales, mode='lines+markers', name='Actual Sales', line=dict(color='blue', width=2) )) fig.add_trace(go.Scatter( x=months, y=targets, mode='lines+markers', name='Target', line=dict(color='green', width=2, dash='dash') )) # Add competitor launch annotation fig.add_vline( x=months[9], line_dash="dash", line_color="red", annotation_text="Competitor Launch" ) fig.update_layout( title="DrugX Sales Trend", xaxis_title="Month", yaxis_title="Sales ($K)", height=300 ) st.plotly_chart(fig, use_container_width=True) elif "competitor" in chart_name.lower(): # Create sample competitor comparison fig = go.Figure() # Market share data months = pd.date_range(start='2023-01-01', periods=12, freq='M') drugx_share = [65, 64, 66, 67, 68, 67, 66, 65, 64, 58, 54, 50] competitor_share = [0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 10, 15] other_share = [35, 36, 34, 33, 32, 33, 34, 35, 36, 37, 36, 35] fig.add_trace(go.Bar( x=months, y=drugx_share, name='DrugX', marker_color='blue' )) fig.add_trace(go.Bar( x=months, y=competitor_share, name='CompDrug2', marker_color='red' )) fig.add_trace(go.Bar( x=months, y=other_share, name='Others', marker_color='gray' )) fig.update_layout( title="Market Share Comparison", xaxis_title="Month", yaxis_title="Market Share (%)", barmode='stack', height=300 ) st.plotly_chart(fig, use_container_width=True) elif "supply" in chart_name.lower(): # Create sample supply chain visualization fig = go.Figure() # Inventory data months = pd.date_range(start='2023-01-01', periods=12, freq='M') inventory = [40, 38, 42, 45, 43, 41, 39, 37, 35, 25, 20, 18] stockouts = [0, 0, 0, 0, 0, 0, 0, 0, 5, 15, 22, 25] fig.add_trace(go.Scatter( x=months, y=inventory, mode='lines+markers', name='Inventory Days', line=dict(color='blue', width=2) )) fig.add_trace(go.Bar( x=months, y=stockouts, name='Stockout %', marker_color='red' )) fig.update_layout( title="Supply Chain Metrics", xaxis_title="Month", yaxis_title="Inventory Days / Stockout %", height=300 ) st.plotly_chart(fig, use_container_width=True) else: # Create a generic chart st.image("https://img.icons8.com/fluency/240/000000/graph.png", width=100) st.markdown(f"*{chart_name}*") # Display key findings if hasattr(card, 'key_findings') and card.key_findings: st.markdown("#### Key Findings") for i, finding in enumerate(card.key_findings): expander = st.expander(f"{finding.get('finding', 'Finding')}") with expander: st.markdown(f"**Details:** {finding.get('details', '')}") if 'evidence' in finding: st.markdown(f"**Evidence:** {finding.get('evidence', '')}") if 'impact' in finding: st.markdown(f"**Impact:** {finding.get('impact', '')}") # Display metrics if hasattr(card, 'metrics') and card.metrics: st.markdown("#### Key Metrics") metric_cols = st.columns(min(len(card.metrics), 4)) for i, (metric_name, metric_value) in enumerate(card.metrics.items()): with metric_cols[i % len(metric_cols)]: st.metric( label=metric_name.replace('_', ' ').title(), value=metric_value ) # Display action items if hasattr(card, 'action_items') and card.action_items: st.markdown("#### Recommended Actions") for i, action in enumerate(card.action_items): priority = action.get('priority', 'Medium') priority_color = { 'High': 'red', 'Medium': 'orange', 'Low': 'blue' }.get(priority, 'gray') st.markdown(f"**{i+1}. {action.get('action', 'Action')}** " f"[{priority}]", unsafe_allow_html=True) action_cols = st.columns(3) with action_cols[0]: st.markdown(f"**Owner:** {action.get('owner', 'TBD')}") with action_cols[1]: st.markdown(f"**Timeline:** {action.get('timeline', 'TBD')}") with action_cols[2]: st.markdown(f"**Expected Impact:** {action.get('expected_impact', 'TBD')}") st.markdown("---") else: st.warning("No insight cards were generated. There might have been an error in the analysis.") # Show detailed workflow state information for debugging with st.expander("Debug: Workflow State", expanded=True): # Basic workflow information st.write("### Basic Workflow Information") st.write(f"Alert: {workflow_state.get('alert', 'Not available')}") st.write(f"Status: {workflow_state.get('status', 'Unknown')}") st.write(f"Plan created: {'Yes' if workflow_state.get('plan') else 'No'}") # Check data sources data_sources = workflow_state.get('data_sources', {}) st.write(f"Data sources: {len(data_sources)} found") if data_sources: st.write("Data source IDs: " + ", ".join(data_sources.keys())) # Check analysis results analysis_results = workflow_state.get('analysis_results', {}) st.write(f"Analysis results: {len(analysis_results)} found") if analysis_results: for key, result in analysis_results.items(): st.write(f"- Analysis {key}: {result.name if hasattr(result, 'name') else 'Unnamed'}") if hasattr(result, 'insights') and result.insights: st.write(f" Insights: {len(result.insights)}") if hasattr(result, 'attribution') and result.attribution: st.write(f" Attribution factors: {len(result.attribution)}") # Check validation results validation_results = workflow_state.get('validation_results', {}) st.write(f"Validation results: {len(validation_results)} found") # Check insight requests insight_requests = workflow_state.get('insight_requests', []) st.write(f"Insight requests: {len(insight_requests)} found") if insight_requests: for i, req in enumerate(insight_requests): st.write(f"- Request {i+1}: ID={req.request_id if hasattr(req, 'request_id') else 'Unknown'}") # Check logs logs = workflow_state.get('logs', []) if logs: st.write("### Workflow Logs") log_df = pd.DataFrame(logs) st.dataframe(log_df) # Show data sources (simplified) with st.expander("View Data Sources"): for source_id, source in workflow_state.get("data_sources", {}).items(): st.markdown(f"**{source.name}**") st.dataframe(source.content.head(5)) # Show code details if enabled if st.session_state.get("show_code", False): with st.expander("View Generated Code"): for analysis_id, analysis in workflow_state.get("analysis_results", {}).items(): st.markdown(f"#### Analysis: {analysis.name}") st.code(analysis.code, language="python") # Debug information for insight requests with st.expander("Debug Insight Requests/Results", expanded=True): insight_requests = workflow_state.get("insight_requests", []) insight_cards = workflow_state.get("insight_cards", {}) # Show insight requests st.write(f"### Insight Requests: {len(insight_requests)}") for i, request in enumerate(insight_requests): st.write(f"#### Request {i+1}: {request.request_id if hasattr(request, 'request_id') else 'Unknown'}") try: # Try to display request details if hasattr(request, 'original_problem'): st.write(f"Problem: {request.original_problem}") # Show truncated analysis results if hasattr(request, 'analysis_results'): st.write("Analysis Results Keys:") st.json({k: type(v).__name__ for k, v in request.analysis_results.items()}) # Show truncated validation results if hasattr(request, 'validation_results'): st.write("Validation Results Keys:") st.json({k: type(v).__name__ for k, v in request.validation_results.items()}) except Exception as e: st.error(f"Error displaying request details: {str(e)}") # Show insight cards st.write(f"### Insight Cards: {len(insight_cards)}") for card_id, card in insight_cards.items(): st.write(f"Card ID: {card_id}") try: st.json({ "title": card.title if hasattr(card, 'title') else "N/A", "description": card.description[:100] + "..." if hasattr(card, 'description') else "N/A", "key_findings": f"{len(card.key_findings)} findings" if hasattr(card, 'key_findings') else "N/A", "charts": card.charts if hasattr(card, 'charts') else "N/A", }) except Exception as e: st.error(f"Error displaying card details: {str(e)}") def initialize_session(): """Initialize session state variables if they don't exist""" if "active_page" not in st.session_state: st.session_state.active_page = "main" if "workflow_state" not in st.session_state: st.session_state.workflow_state = None if "workflow_thread" not in st.session_state: st.session_state.workflow_thread = None if "status_queue" not in st.session_state: st.session_state.status_queue = queue.Queue() if "logs" not in st.session_state: st.session_state.logs = [] if "current_step" not in st.session_state: st.session_state.current_step = None if "alert_submitted" not in st.session_state: st.session_state.alert_submitted = False if "show_sql" not in st.session_state: st.session_state.show_sql = False if "show_code" not in st.session_state: st.session_state.show_code = False if "alert" not in st.session_state: st.session_state.alert = "" if "error_details" not in st.session_state: st.session_state.error_details = []