Spaces:
Build error
Build error
| import gradio as gr | |
| import pandas as pd | |
| import os | |
| import base64 | |
| from pathlib import Path | |
| from data_engine import ( | |
| clean_numeric, run_analysis, create_visualization, handle_missing_data, | |
| undo_last_change, undo_all_changes, download_dataset, | |
| display_data_format, display_text_format | |
| ) | |
| try: | |
| from ai_agent import initialize_llm, analyze_question | |
| except (ImportError, RuntimeError) as e: | |
| print(f"Warning: Full AI agent not available: {e}") | |
| def initialize_llm(): | |
| return None | |
| def analyze_question(question, columns, df, llm): | |
| return "AI agent not available. Please check dependencies.", None, None | |
| from prompts import SAMPLE_QUESTIONS | |
| llm = None | |
| uploaded_df = None | |
| original_df = None | |
| dataset_name = None | |
| change_history = [] | |
| # Fix: Files are in root directory, not in public/ folder | |
| SCRIPT_DIR = Path(__file__).parent.absolute() | |
| logo_path = SCRIPT_DIR / "main-logo.png" # Changed from public/main-logo.png | |
| css_path = SCRIPT_DIR / "style.css" # Changed from public/style.css | |
| def embed_image_base64(path): | |
| """Safely embed image as base64 - returns empty string if file not found""" | |
| try: | |
| if Path(path).exists(): | |
| with open(path, "rb") as f: | |
| return "data:image/png;base64," + base64.b64encode(f.read()).decode() | |
| else: | |
| print(f"Warning: Logo file not found at {path}") | |
| return "" | |
| except Exception as e: | |
| print(f"Warning: Could not load logo: {e}") | |
| return "" | |
| def load_css(path): | |
| """Safely load CSS file""" | |
| try: | |
| if Path(path).exists(): | |
| with open(path, "r", encoding="utf-8") as f: | |
| return f.read() | |
| else: | |
| print(f"Warning: CSS file not found at {path}") | |
| return "" | |
| except Exception as e: | |
| print(f"Warning: Could not load CSS: {e}") | |
| return "" | |
| logo_b64 = embed_image_base64(logo_path) | |
| css = load_css(css_path) | |
| # Base + custom CSS | |
| custom_css = css + """ | |
| .chat-question-input textarea { | |
| min-height: 40px !important; | |
| max-height: 40px !important; | |
| height: 40px !important; | |
| resize: none !important; | |
| } | |
| /* Hide the 'or' text in file upload */ | |
| .gr-file span.or, | |
| span[class*="or"], | |
| .upload-text span { | |
| display: none !important; | |
| } | |
| .gr-file, | |
| .gr-file .wrap, | |
| .gr-file .wrap > div { | |
| position: relative !important; | |
| } | |
| .gr-file svg, | |
| .gr-file .wrap svg, | |
| svg.feather-upload { | |
| position: absolute !important; | |
| top: 50% !important; | |
| left: 50% !important; | |
| transform: translate(-50%, -50%) !important; | |
| width: 60px !important; | |
| height: 60px !important; | |
| opacity: 0.9 !important; | |
| margin: 0 !important; | |
| z-index: 10 !important; | |
| } | |
| .gr-file .wrap span { | |
| opacity: 0 !important; | |
| } | |
| #analysis-type-box { | |
| padding: 12px !important; | |
| min-height: auto !important; | |
| } | |
| #analysis-type-box h3 { | |
| font-size: 16px !important; | |
| margin: 0 0 8px 0 !important; | |
| } | |
| #visualization-box { | |
| padding: 12px !important; | |
| min-height: auto !important; | |
| } | |
| #visualization-box h3 { | |
| font-size: 16px !important; | |
| margin: 0 0 8px 0 !important; | |
| } | |
| /* Column Selector */ | |
| .gr-checkbox-group, | |
| .gr-checkboxgroup { | |
| background: transparent !important; | |
| } | |
| .gr-checkbox-group label, | |
| .gr-checkboxgroup label, | |
| .gr-checkbox-group span, | |
| .gr-checkboxgroup span { | |
| color: #000000 !important; | |
| } | |
| /* Display Format label - white text */ | |
| .gradio-container .contain span[class*="svelte"] { | |
| color: rgb(255, 255, 255) !important; | |
| } | |
| .gradio-container.gradio-container-4-20-0 .contain span[class*="svelte"] { | |
| color: rgb(255, 255, 255) !important; | |
| } | |
| /* Force disable text wrapping in all dataframes */ | |
| .gradio-container table td, | |
| .gradio-container table th, | |
| .dataframe td, | |
| .dataframe th, | |
| table.dataframe td, | |
| table.dataframe th, | |
| .gr-dataframe td, | |
| .gr-dataframe th { | |
| white-space: nowrap !important; | |
| overflow: hidden !important; | |
| text-overflow: ellipsis !important; | |
| } | |
| /* Target Gradio's internal dataframe cells */ | |
| div[class*="table"] td, | |
| div[class*="table"] th, | |
| div[class*="dataframe"] td, | |
| div[class*="dataframe"] th { | |
| white-space: nowrap !important; | |
| overflow: hidden !important; | |
| text-overflow: ellipsis !important; | |
| } | |
| /* Disable wrapping in table data elements */ | |
| .gradio-container [data-testid="table"] td, | |
| .gradio-container [data-testid="table"] th { | |
| white-space: nowrap !important; | |
| overflow: hidden !important; | |
| text-overflow: ellipsis !important; | |
| } | |
| /* ====================================================================== */ | |
| /* FIX WHITE BORDER AROUND ALL DROPDOWNS — ONLY REQUIRED CHANGES APPLIED */ | |
| /* ====================================================================== */ | |
| /* Prevent clipping of borders */ | |
| .wrap, | |
| .wrap-inner, | |
| .secondary-wrap, | |
| .container, | |
| .input-container { | |
| overflow: visible !important; | |
| } | |
| /* The actual dropdown box — apply the white border here */ | |
| .input-container { | |
| border: 2px solid #ffffff !important; | |
| border-radius: 12px !important; | |
| padding: 12px !important; | |
| box-sizing: border-box !important; | |
| background: transparent !important; | |
| min-height: 58px !important; | |
| } | |
| /* Ensure child elements render fully */ | |
| .input-container > * { | |
| overflow: visible !important; | |
| } | |
| /* Remove duplicate border on secondary-wrap */ | |
| .secondary-wrap, | |
| .secondary-wrap.svelte-vomtxz { | |
| border: none !important; | |
| padding: 0 !important; | |
| background: none !important; | |
| } | |
| /* Clean container without affecting layout */ | |
| .svelte-vomtxz.container { | |
| border: none !important; | |
| padding: 0 !important; | |
| margin: 0 !important; | |
| background: none !important; | |
| box-shadow: none !important; | |
| border-radius: 0 !important; | |
| } | |
| /* Remove problematic display: contents */ | |
| .wrap.svelte-vomtxz { | |
| display: block !important; | |
| padding: 0 !important; | |
| border: none !important; | |
| background: none !important; | |
| box-shadow: none !important; | |
| } | |
| /* Simple white border around the dropdown wrapper */ | |
| .wrap-inner.svelte-vomtxz { | |
| border: 2px solid white !important; | |
| border-radius: 8px !important; | |
| box-sizing: border-box !important; | |
| } | |
| /* Force component-37 to be a single-line input box */ | |
| #component-37 { | |
| display: block !important; | |
| visibility: visible !important; | |
| } | |
| #component-37.hide-container { | |
| display: block !important; | |
| visibility: visible !important; | |
| } | |
| #component-37 .block, | |
| #component-37 .svelte-90oupt, | |
| #component-37 .padded { | |
| display: block !important; | |
| visibility: visible !important; | |
| } | |
| #component-37 textarea, | |
| #component-37 input, | |
| #component-37 .gr-textbox textarea, | |
| #component-37 .gr-textbox input { | |
| display: block !important; | |
| visibility: visible !important; | |
| min-height: 45px !important; | |
| max-height: 45px !important; | |
| height: 45px !important; | |
| resize: none !important; | |
| overflow: hidden !important; | |
| line-height: 45px !important; | |
| padding: 0 15px !important; | |
| background: white !important; | |
| border: 1px solid #ccc !important; | |
| border-radius: 8px !important; | |
| color: #333 !important; | |
| } | |
| #component-37 label { | |
| display: none !important; | |
| } | |
| /* Force component-88 to be a single-line input box */ | |
| #component-88 { | |
| display: block !important; | |
| visibility: visible !important; | |
| } | |
| #component-88.hide-container { | |
| display: block !important; | |
| visibility: visible !important; | |
| } | |
| #component-88 .block, | |
| #component-88 .svelte-90oupt, | |
| #component-88 .padded { | |
| display: block !important; | |
| visibility: visible !important; | |
| } | |
| #component-88 textarea, | |
| #component-88 input, | |
| #component-88 .gr-textbox textarea, | |
| #component-88 .gr-textbox input { | |
| display: block !important; | |
| visibility: visible !important; | |
| min-height: 45px !important; | |
| max-height: 45px !important; | |
| height: 45px !important; | |
| resize: none !important; | |
| overflow: hidden !important; | |
| line-height: 45px !important; | |
| padding: 0 15px !important; | |
| background: white !important; | |
| border: 1px solid #ccc !important; | |
| border-radius: 8px !important; | |
| color: #333 !important; | |
| } | |
| #component-88 label { | |
| display: none !important; | |
| } | |
| /* Make Sample Questions heading text black */ | |
| .sample-header, | |
| .sample-header h4, | |
| .sample-header p { | |
| color: #000000 !important; | |
| } | |
| /* Force Enter Your Question h4 text to black */ | |
| .chat-popup-box h4, | |
| .chat-popup-box .gr-markdown h4 { | |
| color: #000000 !important; | |
| } | |
| /* Make entire chat popup box scrollable */ | |
| .chat-popup-box { | |
| overflow-y: auto !important; | |
| } | |
| .chat-popup-box > * { | |
| flex-shrink: 0 !important; | |
| } | |
| /* Make all markdown text in chat popup black */ | |
| .chat-popup-box .gr-markdown, | |
| .chat-popup-box .gr-markdown h1, | |
| .chat-popup-box .gr-markdown h2, | |
| .chat-popup-box .gr-markdown h3, | |
| .chat-popup-box .gr-markdown h4, | |
| .chat-popup-box .gr-markdown h5, | |
| .chat-popup-box .gr-markdown h6, | |
| .chat-popup-box .gr-markdown p { | |
| color: #000000 !important; | |
| } | |
| /* Force component-96 button text to black */ | |
| #component-96-button { | |
| color: #000000 !important; | |
| } | |
| /* Force component-93 button text to black */ | |
| #component-93-button { | |
| color: #000000 !important; | |
| } | |
| /* Force component-98 button text to black */ | |
| #component-98-button { | |
| color: #000000 !important; | |
| } | |
| /* Fix z-index: chat popup should be above how-to-use */ | |
| .chat-popup-box { | |
| z-index: 1001 !important; | |
| } | |
| .how-to-use-sidebar { | |
| z-index: 1000 !important; | |
| } | |
| /* Force all tab buttons text to black */ | |
| .chat-popup-box button, | |
| .chat-popup-box .gr-button, | |
| button[id*="component-"] { | |
| color: #000000 !important; | |
| } | |
| /* Force typed text in textbox to black */ | |
| #component-88 textarea, | |
| #component-88 input { | |
| color: #000000 !important; | |
| } | |
| /* Force Analysis Output label to white */ | |
| #component-19 span.svelte-1gfkn6j { | |
| color: #ffffff !important; | |
| } | |
| /* ====================================================================== */ | |
| /* FORCE WHITE BORDERS AROUND ALL DROPDOWNS AND INPUTS */ | |
| /* ====================================================================== */ | |
| /* Target all Gradio dropdowns */ | |
| .gradio-dropdown, | |
| label.block:has(select), | |
| label.block:has(.dropdown), | |
| div:has(> select), | |
| div:has(> .dropdown) { | |
| border: 2px solid white !important; | |
| border-radius: 8px !important; | |
| padding: 8px !important; | |
| box-sizing: border-box !important; | |
| } | |
| /* Force white borders on all input containers */ | |
| .gr-box, | |
| .gr-input, | |
| .gr-form, | |
| label.block, | |
| .block.svelte-1t38q2d, | |
| .svelte-1t38q2d { | |
| border: 2px solid rgba(255, 255, 255, 0.8) !important; | |
| border-radius: 8px !important; | |
| box-sizing: border-box !important; | |
| } | |
| /* Target dropdown wrappers specifically */ | |
| label:has(select), | |
| div:has(select) { | |
| border: 2px solid white !important; | |
| border-radius: 8px !important; | |
| padding: 4px !important; | |
| } | |
| /* Force borders on File upload component */ | |
| .gr-file, | |
| .file-preview { | |
| border: 2px solid white !important; | |
| border-radius: 8px !important; | |
| } | |
| /* Target checkbox group */ | |
| .gr-checkbox-group, | |
| .gr-checkboxgroup { | |
| border: 2px solid rgba(255, 255, 255, 0.6) !important; | |
| border-radius: 8px !important; | |
| padding: 12px !important; | |
| } | |
| /* Input element styling with white outline */ | |
| /* Target all input elements in Gradio */ | |
| .gradio-container input[type="text"], | |
| .gradio-container input[type="number"], | |
| .gradio-container input[type="email"], | |
| .gradio-container input[type="password"], | |
| .gradio-container textarea, | |
| input[class*="svelte-"] { | |
| outline: 1px solid white !important; | |
| outline-offset: 10px !important; | |
| border: none !important; | |
| } | |
| /* Also target specific Svelte class if it exists */ | |
| input.svelte-1xfxv4t { | |
| margin: var(--spacing-sm); | |
| outline: 1px solid white !important; | |
| outline-offset: 10px; | |
| border: none; | |
| background: inherit; | |
| width: var(--size-full); | |
| color: var(--body-text-color); | |
| font-size: var(--input-text-size); | |
| height: 100%; | |
| } | |
| /* Chat header row with close button */ | |
| .chat-header-row { | |
| position: relative !important; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; | |
| padding: 0 !important; | |
| margin: 0 !important; | |
| border-radius: 20px 20px 0 0 !important; | |
| display: flex !important; | |
| align-items: center !important; | |
| justify-content: space-between !important; | |
| } | |
| .chat-header-row .chat-header { | |
| flex: 1 !important; | |
| padding: 20px 24px !important; | |
| } | |
| /* Close button styling */ | |
| .chat-close-btn, | |
| button.chat-close-btn { | |
| position: absolute !important; | |
| top: 20px !important; | |
| right: 24px !important; | |
| background: rgba(255, 255, 255, 0.2) !important; | |
| border: 2px solid rgba(255, 255, 255, 0.5) !important; | |
| border-radius: 50% !important; | |
| width: 32px !important; | |
| height: 32px !important; | |
| min-width: 32px !important; | |
| min-height: 32px !important; | |
| max-width: 32px !important; | |
| max-height: 32px !important; | |
| color: white !important; | |
| font-size: 20px !important; | |
| font-weight: bold !important; | |
| cursor: pointer !important; | |
| padding: 0 !important; | |
| margin: 0 !important; | |
| display: flex !important; | |
| align-items: center !important; | |
| justify-content: center !important; | |
| transition: all 0.2s ease !important; | |
| z-index: 10 !important; | |
| } | |
| .chat-close-btn:hover, | |
| button.chat-close-btn:hover { | |
| background: rgba(255, 255, 255, 0.3) !important; | |
| transform: scale(1.1) !important; | |
| border-color: rgba(255, 255, 255, 0.7) !important; | |
| } | |
| /* Add JavaScript to make close button work */ | |
| <script> | |
| document.addEventListener('DOMContentLoaded', function() { | |
| // Function to setup close button | |
| function setupCloseButton() { | |
| const closeBtn = document.getElementById('chat-close-btn'); | |
| const chatButtons = document.querySelectorAll('.floating-chat-btn'); | |
| if (closeBtn && chatButtons.length > 0) { | |
| closeBtn.addEventListener('click', function(e) { | |
| e.preventDefault(); | |
| e.stopPropagation(); | |
| // Click the floating chat button to toggle the popup | |
| chatButtons[0].click(); | |
| }); | |
| } else { | |
| // If elements not found, try again after a short delay | |
| setTimeout(setupCloseButton, 100); | |
| } | |
| } | |
| // Start setup | |
| setupCloseButton(); | |
| // Also observe for when the popup becomes visible | |
| const observer = new MutationObserver(function() { | |
| setupCloseButton(); | |
| }); | |
| observer.observe(document.body, { | |
| childList: true, | |
| subtree: true, | |
| attributes: true, | |
| attributeFilter: ['style', 'class'] | |
| }); | |
| }); | |
| </script> | |
| /* Show warning when no dataset */ | |
| .no-dataset-warning { | |
| display: block !important; | |
| background: #fff3cd; | |
| border: 2px solid #ffc107; | |
| border-radius: 8px; | |
| padding: 12px; | |
| margin-bottom: 15px; | |
| } | |
| .no-dataset-warning p { | |
| color: #856404 !important; | |
| margin: 0; | |
| } | |
| """ | |
| def enable_chat_features(file): | |
| """Enable chat features only after dataset upload""" | |
| if file is None: | |
| # Disable submit button and show warning | |
| return gr.update(interactive=False) | |
| else: | |
| # Enable submit button | |
| return gr.update(interactive=True) | |
| def upload_dataset(file): | |
| global uploaded_df, original_df, dataset_name | |
| if file is None: | |
| return "No file uploaded", gr.update(visible=False), gr.update(choices=[]), gr.update(visible=False) | |
| try: | |
| dataset_name = os.path.basename(file.name) | |
| if file.name.endswith('.csv'): | |
| uploaded_df = pd.read_csv(file.name) | |
| elif file.name.endswith(('.xlsx', '.xls')): | |
| uploaded_df = pd.read_excel(file.name) | |
| else: | |
| return "Unsupported file format. Please upload CSV or Excel files.", gr.update(visible=False), gr.update(choices=[]), gr.update(visible=False) | |
| uploaded_df = clean_numeric(uploaded_df) | |
| original_df = uploaded_df.copy() | |
| info_text = f"✓ Dataset Loaded: {dataset_name} ({uploaded_df.shape[0]} rows × {uploaded_df.shape[1]} columns)" | |
| return info_text, gr.update(visible=False), gr.update(choices=list(uploaded_df.columns), value=[]), gr.update(visible=True) | |
| except Exception as e: | |
| return f"Error loading file: {str(e)}", gr.update(visible=False), gr.update(choices=[]), gr.update(visible=False) | |
| def clear_dataset(): | |
| global uploaded_df, original_df, dataset_name, change_history | |
| uploaded_df = None | |
| original_df = None | |
| dataset_name = None | |
| change_history = [] | |
| return ( | |
| "Dataset cleared. Please upload a new file.", | |
| gr.update(visible=False), | |
| gr.update(choices=[], value=[]), | |
| gr.update(visible=False), | |
| gr.update(interactive=False) # Disable submit button | |
| ) | |
| def update_preview(format_type, selected_columns): | |
| if uploaded_df is None or format_type == "None": | |
| return gr.update(visible=False), gr.update(visible=False), gr.update(visible=False) | |
| elif format_type == "DataFrame": | |
| return gr.update(value=display_data_format(format_type, selected_columns, uploaded_df), visible=True), gr.update(visible=False), gr.update(visible=True) | |
| else: | |
| return gr.update(visible=False), gr.update(value=display_text_format(format_type, selected_columns, uploaded_df), visible=True), gr.update(visible=True) | |
| def handle_analysis_change(analysis_type, selected_columns): | |
| if uploaded_df is None or analysis_type == "None": | |
| return gr.update(value="", visible=False), gr.update(visible=False), gr.update(visible=False) | |
| result_text, data_table = run_analysis(analysis_type, selected_columns, uploaded_df) | |
| if result_text and result_text.strip(): | |
| if data_table is not None: | |
| return gr.update(value=result_text, visible=True), gr.update(visible=True), gr.update(value=data_table, visible=True) | |
| else: | |
| return gr.update(value=result_text, visible=True), gr.update(visible=True), gr.update(visible=False) | |
| else: | |
| return gr.update(value="", visible=False), gr.update(visible=False), gr.update(visible=False) | |
| def handle_viz_change(viz_type, selected_columns): | |
| if uploaded_df is None or viz_type == "None": | |
| return None, gr.update(visible=False), "", gr.update(visible=False) | |
| result = create_visualization(viz_type, selected_columns, uploaded_df) | |
| if result and len(result) == 3: | |
| fig, explanation, chart_obj = result | |
| if explanation and fig is not None: | |
| return fig, gr.update(visible=True), explanation, gr.update(visible=True) | |
| else: | |
| return None, gr.update(visible=False), explanation or "Error in visualization", gr.update(visible=False) | |
| else: | |
| return None, gr.update(visible=False), "Error in visualization", gr.update(visible=False) | |
| def handle_question_analysis(question): | |
| global uploaded_df, llm | |
| # CRITICAL CHECK #1: No dataset uploaded | |
| if uploaded_df is None or len(uploaded_df) == 0: | |
| error_msg = "⚠️ **NO DATASET LOADED**\n\nPlease upload a CSV or Excel file before asking questions.\n\nSteps:\n1. Click 'DROP YOUR FILE HERE' on the left\n2. Select your dataset\n3. Wait for confirmation\n4. Then ask your question" | |
| return error_msg, None, None | |
| # CRITICAL CHECK #2: Empty question | |
| if not question or question.strip() == "": | |
| return "⚠️ **EMPTY QUESTION**\n\nPlease type a question about your data.", None, None | |
| # CRITICAL CHECK #3: LLM not initialized | |
| if llm is None: | |
| return "⚠️ **AI ERROR**\n\nAI agent not initialized. Please restart the application or check your GROQ_API_KEY.", None, None | |
| # All checks passed - proceed with analysis | |
| return analyze_question(question, [], uploaded_df, llm) | |
| with gr.Blocks() as demo: | |
| # Only show logo if it exists | |
| logo_html = f'<img src="{logo_b64}" width="120" style="margin-bottom:70px; margin-top: 30px; opacity:0.95;">' if logo_b64 else '' | |
| gr.HTML(f""" | |
| <div class="header-box"> | |
| <div style="flex:1; display:flex; justify-content:flex-start; width: 100px;"> | |
| {logo_html} | |
| </div> | |
| <div style="flex:1; display:flex; justify-content:center;"> | |
| <h1 class="header-title">SparkNova</h1> | |
| </div> | |
| <div style="flex:1;"></div> | |
| </div> | |
| <div style="text-align: center; display: flex; justify-content: center;"> | |
| <p style="margin-top:20px; font-size:1.30em; opacity:1; color: white; max-width: 1450px; line-height: 1.45;"> | |
| SparkNova is a data analysis platform that allows users to upload datasets, explore insights, visualize patterns, and ask questions about their data. | |
| It simplifies data analytics by automating cleaning, visualization, and intelligent interpretation for quick decision-making. | |
| </p> | |
| </div> | |
| """) | |
| gr.HTML(""" | |
| <div id="dataset-warning" style="background: #fff3cd; border: 2px solid #ffc107; border-radius: 8px; padding: 12px; margin-bottom: 15px; display: none;"> | |
| <p style="margin: 0; color: #856404; font-weight: bold;">⚠️ No dataset uploaded</p> | |
| <p style="margin: 5px 0 0 0; color: #856404; font-size: 0.9em;">Please upload a dataset first before asking questions.</p> | |
| </div> | |
| """) | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| # How to Use Box at the top | |
| gr.HTML(""" | |
| <div style="background: rgba(255, 255, 255, 0.1); border-radius: 12px; padding: 20px; margin-bottom: 20px; border: 2px solid rgba(255, 255, 255, 0.2);"> | |
| <h3 style="color: white !important; margin-top: 0; margin-bottom: 15px; font-size: 1.2em;">How to Use</h3> | |
| <ul style="color: white !important; line-height: 1.8; margin: 0; padding-left: 20px;"> | |
| <li style="color: white !important;">Use Display Format to preview your data.</li> | |
| <li style="color: white !important;">Select an Analysis Type.</li> | |
| <li style="color: white !important;">Choose a Visualization Type.</li> | |
| <li style="color: white !important;">Ask any question about your data.</li> | |
| </ul> | |
| </div> | |
| """) | |
| gr.Markdown("### Upload Dataset") | |
| file_input = gr.File(label="DROP YOUR FILE HERE", file_types=[".csv", ".xlsx", ".xls"]) | |
| dataset_info = gr.Markdown() | |
| with gr.Row(): | |
| clear_btn = gr.Button("Clear Dataset", variant="secondary", size="sm") | |
| format_selector = gr.Dropdown( | |
| choices=["None", "DataFrame", "JSON", "Dictionary"], | |
| value="None", | |
| label="Display Format" | |
| ) | |
| column_selector = gr.CheckboxGroup( | |
| label="Select Columns", | |
| choices=[], | |
| visible=False | |
| ) | |
| gr.Markdown("### Choose an Analysis Type") | |
| analysis_selector = gr.Dropdown( | |
| choices=["None", "Summary", "Describe", "Top 5 Rows", "Bottom 5 Rows", "Missing Values", "Group & Aggregate", "Calculate Expressions", "Highest Correlation"], | |
| value="None", | |
| label="Analysis Type" | |
| ) | |
| gr.Markdown("### Visualization Types") | |
| viz_selector = gr.Dropdown( | |
| choices=["None", "Bar Chart", "Line Chart", "Scatter Plot", "Pie Chart", "Histogram", "Box Plot", "Heat Map"], | |
| value="None", | |
| label="Chart Type" | |
| ) | |
| with gr.Column(scale=2): | |
| preview_heading = gr.Markdown("### Dataset Preview", visible=False) | |
| dataset_preview = gr.Dataframe(wrap=True, visible=False) | |
| text_preview = gr.Textbox(label="Text Preview", lines=15, visible=False) | |
| analysis_heading = gr.Markdown("### Analysis Results", visible=False) | |
| analysis_output = gr.Textbox(label="Analysis Output", lines=10, visible=False, interactive=False) | |
| analysis_data_table = gr.Dataframe(label="Data Table", wrap=True, visible=False) | |
| chart_output_new = gr.Plot(label="Chart", visible=False) | |
| chart_explanation = gr.Textbox(label="Chart Analysis", lines=5, visible=False, interactive=False) | |
| # Floating Chat Button | |
| chat_toggle_btn = gr.Button("💬", elem_classes=["floating-chat-btn"], size="sm") | |
| with gr.Column(visible=False, elem_classes=["chat-popup-box"]) as chat_popup: | |
| with gr.Row(elem_classes=["chat-header-row"]): | |
| gr.HTML("<div class='chat-header'><h3>Ask SparkNova</h3></div>") | |
| close_chat_btn = gr.Button("✕", elem_classes=["chat-close-btn"], size="sm") | |
| gr.Markdown("#### Sample Questions", elem_classes=["sample-header"]) | |
| for i in range(min(5, len(SAMPLE_QUESTIONS))): | |
| gr.Markdown(f"• {SAMPLE_QUESTIONS[i]}", elem_classes=["sample-q-text"]) | |
| gr.Markdown("#### Enter Your Question") | |
| user_question = gr.Textbox( | |
| label="Your Question", | |
| placeholder="Type your question here...", | |
| lines=2, | |
| max_lines=3, | |
| elem_classes=["chat-question-input"], | |
| interactive=True, | |
| show_label=True | |
| ) | |
| submit_btn = gr.Button("Analyze", variant="primary", size="lg", elem_classes=["chat-submitbtn"], interactive=False) | |
| gr.HTML("<div class='response-box'><h4>Analysis Results</h4></div>") | |
| with gr.Tabs(): | |
| with gr.Tab("Response"): | |
| output_text = gr.Textbox(label="", interactive=False, lines=10) | |
| with gr.Tab("Visualization"): | |
| chart_output = gr.Plot(label="") | |
| with gr.Tab("Data"): | |
| result_table = gr.Dataframe(label="", wrap=True) | |
| chat_visible = [False] | |
| def toggle_chat(): | |
| chat_visible[0] = not chat_visible[0] | |
| return gr.update(visible=chat_visible[0]) | |
| def close_chat(): | |
| chat_visible[0] = False | |
| return gr.update(visible=False) | |
| chat_toggle_btn.click(toggle_chat, inputs=None, outputs=chat_popup) | |
| close_chat_btn.click(close_chat, inputs=None, outputs=chat_popup) | |
| file_input.change( | |
| upload_dataset, | |
| inputs=file_input, | |
| outputs=[dataset_info, dataset_preview, column_selector, column_selector] | |
| ).then( | |
| enable_chat_features, | |
| inputs=file_input, | |
| outputs=submit_btn | |
| ) | |
| clear_btn.click( | |
| clear_dataset, | |
| outputs=[dataset_info, dataset_preview, column_selector, column_selector, submit_btn] | |
| ) | |
| format_selector.change(update_preview, inputs=[format_selector, column_selector], outputs=[dataset_preview, text_preview, preview_heading]) | |
| column_selector.change(update_preview, inputs=[format_selector, column_selector], outputs=[dataset_preview, text_preview, preview_heading]) | |
| analysis_selector.change(handle_analysis_change, inputs=[analysis_selector, column_selector], outputs=[analysis_output, analysis_heading, analysis_data_table]) | |
| column_selector.change(handle_analysis_change, inputs=[analysis_selector, column_selector], outputs=[analysis_output, analysis_heading, analysis_data_table]) | |
| viz_selector.change(handle_viz_change, inputs=[viz_selector, column_selector], outputs=[chart_output_new, chart_output_new, chart_explanation, chart_explanation]) | |
| column_selector.change(handle_viz_change, inputs=[viz_selector, column_selector], outputs=[chart_output_new, chart_output_new, chart_explanation, chart_explanation]) | |
| submit_btn.click(handle_question_analysis, inputs=[user_question], outputs=[output_text, chart_output, result_table]) | |
| gr.HTML("<div style='text-align: center; margin-top: 20px; color: #666;'>Powered by GROQ LLM & Gradio</div>") | |
| if __name__ == "__main__": | |
| llm = initialize_llm() | |
| if not llm: | |
| print("Warning: Failed to initialize GROQ API") | |
| demo.launch(show_error=True, share=False, theme=gr.themes.Soft(), css=custom_css) |