Spaces:
Sleeping
Sleeping
File size: 10,200 Bytes
d853cbf 110ec6e d853cbf 110ec6e d853cbf | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 | """
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()
|