Spaces:
Sleeping
Sleeping
| """ | |
| Excel Analyst Agent - Gradio Application | |
| Main entry point for the web interface | |
| """ | |
| import os | |
| import sys | |
| import subprocess | |
| # Fix mcp version conflict: HuggingFace Spaces installs gradio[mcp] which downgrades mcp to 1.10.1 | |
| # We need mcp>=1.17.0 for fastmcp, so we reinstall it here to ensure correct version | |
| # This runs at startup before importing any modules that depend on mcp | |
| try: | |
| subprocess.check_call([sys.executable, "-m", "pip", "install", "--upgrade", "mcp>=1.17.0", "--quiet"], | |
| stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) | |
| except subprocess.CalledProcessError: | |
| # If installation fails, continue anyway - it might already be the right version | |
| pass | |
| import logging | |
| import base64 | |
| from io import BytesIO | |
| from typing import Optional, Tuple, List | |
| import gradio as gr | |
| import pandas as pd | |
| from PIL import Image | |
| from dotenv import load_dotenv | |
| from app_agents.master_agent import MasterAgent | |
| # Load environment variables | |
| load_dotenv() | |
| # Configure logging | |
| logging.basicConfig( | |
| level=logging.INFO, | |
| format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' | |
| ) | |
| logger = logging.getLogger(__name__) | |
| # Get OpenAI API key | |
| OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") | |
| if not OPENAI_API_KEY: | |
| logger.warning("OPENAI_API_KEY not found in environment variables") | |
| def process_analysis( | |
| file: Optional[gr.File], | |
| query: str, | |
| api_key: Optional[str] = None | |
| ) -> Tuple[str, Optional[pd.DataFrame], Optional[List[Image.Image]]]: | |
| """ | |
| Process the user's file and query | |
| Args: | |
| file: Uploaded file object | |
| query: User's natural language query | |
| api_key: Optional API key override | |
| Returns: | |
| Tuple of (output_text, dataframe, images) | |
| """ | |
| try: | |
| # Validate inputs | |
| if not file: | |
| return "β Please upload an Excel (.xlsx) or CSV (.csv) file.", None, None | |
| if not query or query.strip() == "": | |
| return "β Please enter a query describing what you want to analyze.", None, None | |
| # Get API key | |
| used_api_key = api_key if api_key else OPENAI_API_KEY | |
| # Log presence (masked) of API key from UI/env for diagnostics | |
| if api_key: | |
| masked = f"{api_key[:4]}...{api_key[-4:]}" if len(api_key) >= 8 else "***" | |
| logger.info(f"API key provided via UI: True (masked: {masked})") | |
| else: | |
| logger.info(f"API key provided via UI: False") | |
| if OPENAI_API_KEY: | |
| masked_env = f"{OPENAI_API_KEY[:4]}...{OPENAI_API_KEY[-4:]}" if len(OPENAI_API_KEY) >= 8 else "***" | |
| logger.info(f"Using OPENAI_API_KEY from env: True (masked: {masked_env})") | |
| else: | |
| logger.info("Using OPENAI_API_KEY from env: False") | |
| if not used_api_key: | |
| return "β Please provide an OpenAI API key either in the interface or as an environment variable (OPENAI_API_KEY).", None, None | |
| # Get file path | |
| file_path = file.name | |
| logger.info(f"Processing file: {file_path}") | |
| logger.info(f"User query: {query}") | |
| # Validate file extension | |
| if not (file_path.endswith('.xlsx') or file_path.endswith('.csv')): | |
| return "β Please upload a valid Excel (.xlsx) or CSV (.csv) file.", None, None | |
| # Initialize the master agent | |
| logger.info("Initializing Master Agent...") | |
| agent = MasterAgent(api_key=used_api_key, model="gpt-4o-mini") | |
| # Analyze the file | |
| logger.info("Starting analysis...") | |
| result = agent.analyze(user_query=query, file_path=file_path) | |
| if not result['success']: | |
| error_msg = result.get('error', 'Unknown error occurred') | |
| return f"β Analysis failed:\n\n{error_msg}", None, None | |
| # Format output | |
| output_parts = ["β **Analysis Complete**\n"] | |
| # Add text output | |
| if result['output']: | |
| output_parts.append("### Results:\n") | |
| output_parts.append(result['output']) | |
| output_parts.append("\n") | |
| # Add code if available | |
| if result['code']: | |
| output_parts.append("\n### Generated Code:\n") | |
| output_parts.append("```python\n") | |
| output_parts.append(result['code']) | |
| output_parts.append("\n```\n") | |
| output_text = "\n".join(output_parts) | |
| # Prepare dataframe | |
| df_output = None | |
| if result['dataframe']: | |
| try: | |
| df_output = pd.DataFrame(result['dataframe']) | |
| logger.info(f"Dataframe prepared: {len(df_output)} rows") | |
| except Exception as e: | |
| logger.error(f"Error preparing dataframe: {e}") | |
| output_text += f"\n\nβ οΈ Note: Could not display dataframe - {str(e)}" | |
| # Prepare images | |
| images_output = None | |
| if result['images']: | |
| try: | |
| images_output = [] | |
| for img_base64 in result['images']: | |
| img_data = base64.b64decode(img_base64) | |
| img = Image.open(BytesIO(img_data)) | |
| images_output.append(img) | |
| logger.info(f"Prepared {len(images_output)} images") | |
| except Exception as e: | |
| logger.error(f"Error preparing images: {e}") | |
| output_text += f"\n\nβ οΈ Note: Could not display images - {str(e)}" | |
| return output_text, df_output, images_output | |
| except Exception as e: | |
| error_msg = f"Unexpected error: {str(e)}" | |
| logger.error(error_msg, exc_info=True) | |
| return f"β {error_msg}", None, None | |
| def create_interface() -> gr.Blocks: | |
| """ | |
| Create the Gradio interface | |
| Returns: | |
| Gradio Blocks interface | |
| """ | |
| with gr.Blocks( | |
| title="Excel Analyst Agent", | |
| theme=gr.themes.Soft() | |
| ) as interface: | |
| gr.Markdown( | |
| """ | |
| # π Excel Analyst Agent | |
| **Intelligent data analysis powered by AI** | |
| Upload your Excel or CSV file and describe what you want to analyze in plain English. | |
| The agent will generate and execute Python code to fulfill your request. | |
| ### Features: | |
| - π Data analysis and statistics | |
| - π Automatic visualizations | |
| - π Natural language queries | |
| - π€ Powered by OpenAI GPT-4o-mini | |
| ### Example queries: | |
| - *"Show me the average sales per region and create a bar chart"* | |
| - *"Find the top 10 customers by revenue"* | |
| - *"Calculate monthly trends and visualize them"* | |
| - *"Identify outliers in the price column"* | |
| """ | |
| ) | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| gr.Markdown("### π Input") | |
| file_input = gr.File( | |
| label="Upload Excel or CSV file", | |
| file_types=[".xlsx", ".csv"], | |
| type="filepath" | |
| ) | |
| query_input = gr.Textbox( | |
| label="What would you like to analyze?", | |
| placeholder="E.g., Show me the average sales per region and create a bar chart", | |
| lines=3 | |
| ) | |
| api_key_input = gr.Textbox( | |
| label="OpenAI API Key (optional if set in environment)", | |
| placeholder="sk-...", | |
| type="password" | |
| ) | |
| with gr.Row(): | |
| submit_btn = gr.Button("π Analyze", variant="primary", size="lg") | |
| clear_btn = gr.ClearButton( | |
| components=[file_input, query_input, api_key_input], | |
| value="π Clear" | |
| ) | |
| with gr.Column(scale=2): | |
| gr.Markdown("### π Results") | |
| output_text = gr.Markdown( | |
| label="Analysis Output", | |
| value="Results will appear here..." | |
| ) | |
| output_dataframe = gr.Dataframe( | |
| label="Data Preview", | |
| interactive=False, | |
| wrap=True | |
| ) | |
| output_images = gr.Gallery( | |
| label="Visualizations", | |
| columns=2, | |
| height="auto" | |
| ) | |
| gr.Markdown( | |
| """ | |
| --- | |
| ### π‘ Tips: | |
| - Be specific in your queries for better results | |
| - The agent can create multiple visualizations in one request | |
| - If something doesn't work, try rephrasing your query | |
| - All processing is done securely in a sandboxed environment | |
| ### π Privacy: | |
| - Your files are processed temporarily and not stored | |
| - Code execution is sandboxed without internet access | |
| - Only you and OpenAI's API see your data | |
| """ | |
| ) | |
| # Connect the submit button | |
| submit_btn.click( | |
| fn=process_analysis, | |
| inputs=[file_input, query_input, api_key_input], | |
| outputs=[output_text, output_dataframe, output_images] | |
| ) | |
| # Also allow Enter key to submit | |
| query_input.submit( | |
| fn=process_analysis, | |
| inputs=[file_input, query_input, api_key_input], | |
| outputs=[output_text, output_dataframe, output_images] | |
| ) | |
| return interface | |
| def main(): | |
| """ | |
| Main entry point | |
| """ | |
| logger.info("Starting Excel Analyst Agent application...") | |
| # Create and launch the interface | |
| interface = create_interface() | |
| interface.launch( | |
| server_name="0.0.0.0", | |
| server_port=7860, | |
| share=False, | |
| show_error=True | |
| ) | |
| if __name__ == "__main__": | |
| main() | |