#!/usr/bin/env python3 """ Universal Multi-Agent Platform - Core Application (Production Ready) Auto-generated with Gradio 4.x compatibility """ import gradio as gr import pandas as pd from typing import Dict, Any, List, Optional, Tuple from pathlib import Path import json import os # ============================================================================ # IMPORT ENABLED PLUGINS # ============================================================================ from plugins.processors.schema_detector import * from plugins.processors.text_processor import * from plugins.outputs.table_formatter import * from plugins.processors.date_normalizer import * from plugins.file_handlers.csv_handler import * from plugins.outputs.report_generator import * from plugins.file_handlers.excel_handler import * from plugins.memory.document_memory import * from plugins.processors.data_cleaner import * from plugins.analyzers.statistical_analyzer import * from plugins.analyzers.time_series_analyzer import * from plugins.outputs.chart_generator import * from plugins.memory.conversation_memory import * # ============================================================================ # PLUGIN MANAGER (Handles all plugin interactions) # ============================================================================ class PluginManager: """Manage all plugins and application state.""" def __init__(self): # Initialize file handlers self.file_handlers = [] self.file_handlers.append(CSVHandler()) self.file_handlers.append(ExcelHandler()) # Initialize processors/analyzers self.data_cleaner = DataCleaner() if True else None self.time_series_analyzer = TimeSeriesAnalyzer() if True else None self.statistical_analyzer = StatisticalAnalyzer() if True else None # Initialize memory/outputs self.conversation_memory = ConversationMemory() if True else None self.table_formatter = TableFormatter() if True else None self.chart_generator = ChartGenerator() if True else None # Data storage self.loaded_data: Optional[Dict[str, Any]] = None self.cleaned_df: Optional[pd.DataFrame] = None self.last_chart_json: Optional[str] = None def load_file(self, file_path: str) -> Dict[str, Any]: """Load file using appropriate handler and automatically clean data.""" self.loaded_data = None self.cleaned_df = None self.last_chart_json = None if not os.path.exists(file_path): return {"success": False, "error": "File not found on server"} for handler in self.file_handlers: if handler.can_handle(file_path): result = handler.load(file_path) if result.get("success"): self.loaded_data = result # Auto-clean tabular data df = self._get_raw_df() if df is not None and self.data_cleaner: df = self.data_cleaner.clean_dataframe(df) self.cleaned_df = self.data_cleaner.enforce_schema(df) if "metadata" not in result: result["metadata"] = {} result["metadata"]["cleaned_shape"] = list(self.cleaned_df.shape) result["metadata"]["cleaned_cols"] = list(self.cleaned_df.columns) return result return {"success": False, "error": "No handler found for this file type"} def _get_raw_df(self) -> Optional[pd.DataFrame]: """Internal method to extract a DataFrame from loaded_data.""" if not self.loaded_data: return None if "combined" in self.loaded_data and isinstance(self.loaded_data["combined"], pd.DataFrame): return self.loaded_data["combined"] elif "data" in self.loaded_data and isinstance(self.loaded_data["data"], pd.DataFrame): return self.loaded_data["data"] return None # Initialize plugin manager pm = PluginManager() # ============================================================================ # GRADIO INTERFACE LOGIC # ============================================================================ def upload_file(file): """Handle file upload.""" if file is None: return "❌ No file uploaded", None try: result = pm.load_file(file.name) if result.get("success"): # Get appropriate handler for preview preview_html = "Data loaded successfully" for handler in pm.file_handlers: if handler.can_handle(file.name) and hasattr(handler, 'preview'): preview_html = handler.preview(result) break shape_info = f"Shape: {pm.cleaned_df.shape}" if pm.cleaned_df is not None else "Non-tabular data" summary = "✅ File loaded and processed successfully\n" summary += f"Type: {result.get('file_type', 'unknown')}\n" summary += f"Data: {shape_info}\n\n" summary += "Ready for conversational analysis!" return summary, preview_html return f"❌ Error: {result.get('error')}", None except Exception as e: return f"❌ Critical Error: {str(e)}", None def process_query(query: str, history: List) -> Tuple[List, str, Optional[str]]: """ Executes conversational analytics. Returns: updated history, empty query text, and chart JSON. """ if not query or not query.strip(): return history + [("", "❌ Please enter a question")], "", None if pm.conversation_memory: pm.conversation_memory.add_message("user", query) df = pm.cleaned_df pm.last_chart_json = None # Handle No Data Case if df is None or df.empty: # Check if non-tabular data was loaded if pm.loaded_data and pm.loaded_data.get('file_type') in ['pdf', 'docx']: document_text = pm.loaded_data.get('text', '') or str(pm.loaded_data.get('text_data', [{}])[0].get('text', 'No text')) response = "📄 **Document Content Loaded**\n\n" response += "The system has loaded a document. Advanced NLP analysis would be applied here.\n" response += f"Text Sample: {document_text[:200]}..." else: response = "❌ No **data** loaded for analysis. Please upload a file first." if pm.conversation_memory: pm.conversation_memory.add_message("assistant", response) return history + [(query, response)], "", None try: # Execute Analytics if pm.time_series_analyzer: description, result_df = pm.time_series_analyzer.analyze_query(df, query) elif pm.statistical_analyzer: stats = pm.statistical_analyzer.analyze(df) description = "📊 Statistical Analysis Results" result_df = pd.DataFrame(stats.get('columns', {})).T else: description = "⚠️ No analyzer available. Upload data and try basic queries." result_df = None final_response = f"**Query:** {query}\n\n{description}\n\n" chart_json = None if result_df is not None and not result_df.empty: # Format Table Output if pm.table_formatter: table_markdown = pm.table_formatter.format_to_markdown(result_df.head(10)) final_response += "### Results (Top 10 Rows):\n" final_response += table_markdown final_response += f"\n\n*Total Rows: {len(result_df):,}*" # Generate Chart Output if pm.chart_generator and len(result_df.columns) >= 2: try: x_col = result_df.columns[0] y_col = result_df.columns[1] chart_json = pm.chart_generator.create_chart_html( result_df.head(20), 'bar', x=x_col, y=y_col, title=description.split('\n')[0][:50] ) except Exception as chart_err: print(f"Chart generation failed: {chart_err}") else: final_response = f"**Query:** {query}\n\n{description}" if pm.conversation_memory: pm.conversation_memory.add_message("assistant", final_response) return history + [(query, final_response)], "", chart_json except Exception as e: import traceback error_trace = traceback.format_exc() response = f"❌ Analysis Error: {str(e)}\n\nDebug Info:\n```\n{error_trace[:500]}\n```" return history + [(query, response)], "", None def create_ui(): """Create Gradio interface (Gradio 4.x compatible).""" with gr.Blocks(title="Universal AI Platform", theme=gr.themes.Soft()) as demo: gr.Markdown("# 🤖 Universal Multi-Agent Platform") gr.Markdown("## AI-Powered Analysis & Conversational Intelligence") with gr.Tabs(): # ================================================================ # FILE UPLOAD TAB # ================================================================ with gr.Tab("📁 Upload & Process"): with gr.Row(): with gr.Column(scale=1): file_upload = gr.File( label="Upload Your File", file_types=[".xlsx", ".xls", ".csv", ".pdf", ".docx", ".json", ".xml"], interactive=True ) upload_btn = gr.Button("📤 Process File", variant="primary", size="lg") upload_status = gr.Textbox( label="Status", lines=8, value="Ready to process files. Supported: Excel, CSV, PDF, Word, JSON, XML", interactive=False ) with gr.Column(scale=2): data_preview = gr.HTML(label="Data Preview") upload_btn.click( fn=upload_file, inputs=[file_upload], outputs=[upload_status, data_preview] ) # ================================================================ # CHAT INTERFACE TAB # ================================================================ with gr.Tab("💬 Ask Questions"): chatbot = gr.Chatbot( height=450, label="Conversational AI Assistant", type='tuples', show_copy_button=True ) gr.Markdown(""" ### 📝 Example Queries: - "Summarize the data" - "Show me aggregated statistics" - "Group by [column name]" - "Segment the data into categories" - "Analyze trends over time" - "Show correlation between columns" """) with gr.Row(): msg = gr.Textbox( label="Your Query", placeholder="Ask anything about your data...", scale=4, lines=2 ) submit_btn = gr.Button("Send", variant="primary", scale=1, size="lg") # Chart display area chart_display = gr.HTML( label="Visualization", value="" ) # Clear button with gr.Row(): clear_btn = gr.Button("🗑️ Clear Chat", variant="secondary") def process_and_display(query: str, history: List) -> Tuple[List, str, str]: """Process query and return chart HTML.""" updated_history, empty_msg, chart_json_str = process_query(query, history) # Convert chart JSON to HTML with embedded Plotly # KEY FIX: Use string concatenation instead of f-string substitution chart_html = "" if chart_json_str: # Build the HTML string using concatenation to avoid f-string issues chart_html = ( '