File size: 7,431 Bytes
37336a7
bcd9ccf
882008c
 
 
 
 
 
e7486fb
fc29f53
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37336a7
fc29f53
 
882008c
 
 
 
 
 
 
 
 
fc29f53
882008c
37336a7
882008c
 
 
 
 
 
 
fc29f53
 
 
 
 
 
 
882008c
 
fc29f53
 
37336a7
fc29f53
 
 
882008c
 
 
 
 
 
fc29f53
 
37336a7
fc29f53
 
 
 
 
37336a7
fc29f53
37336a7
fc29f53
 
 
 
 
 
 
 
 
 
 
882008c
 
fc29f53
882008c
 
fc29f53
882008c
 
e7486fb
882008c
 
 
 
fc29f53
882008c
 
 
 
fc29f53
882008c
 
 
 
 
fc29f53
882008c
 
 
 
 
 
 
 
 
 
 
 
 
 
fc29f53
 
 
 
 
 
 
 
882008c
 
 
 
fc29f53
882008c
 
 
 
fc29f53
882008c
fc29f53
882008c
 
fc29f53
 
 
 
 
882008c
 
 
37336a7
 
882008c
 
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
import os
import pandas as pd
import requests
import json
import subprocess
import gradio as gr
import tempfile
import sys
from io import StringIO
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
from typing import Dict, Any, Tuple, Optional
import ast

# Safe imports list - mirrors smolagents approach
SAFE_IMPORTS = [
    "pandas", "numpy", "matplotlib", "seaborn", "sklearn", 
    "scipy", "statsmodels", "plotly", "math", "datetime",
    "collections", "itertools", "functools", "operator"
]

class SafeExecutor:
    """Safely executes Python code with restricted imports and environment"""
    
    def __init__(self, allowed_imports=None):
        self.allowed_imports = allowed_imports or SAFE_IMPORTS
        
    def validate_imports(self, code: str) -> bool:
        """Validate that all imports in the code are allowed"""
        try:
            tree = ast.parse(code)
            for node in ast.walk(tree):
                if isinstance(node, (ast.Import, ast.ImportFrom)):
                    for name in node.names:
                        module = name.name.split('.')[0]
                        if module not in self.allowed_imports:
                            raise ValueError(f"Import of '{module}' is not allowed. Allowed imports: {self.allowed_imports}")
            return True
        except Exception as e:
            raise ValueError(f"Code validation error: {str(e)}")

    def execute_code(self, code: str, globals_dict: Dict[str, Any] = None) -> Tuple[Any, str]:
        """Execute code safely and return the output"""
        if globals_dict is None:
            globals_dict = {}
            
        # Add safe imports to globals
        for module in self.allowed_imports:
            try:
                globals_dict[module] = __import__(module)
            except ImportError:
                pass

        # Redirect stdout to capture print outputs
        old_stdout = sys.stdout
        redirected_output = StringIO()
        sys.stdout = redirected_output

        try:
            # Validate imports first
            self.validate_imports(code)
            
            # Execute the code
            exec(code, globals_dict)
            output = redirected_output.getvalue()
            
            # Handle matplotlib figures
            if plt.get_figs():
                with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as tmp:
                    plt.savefig(tmp.name)
                    plt.close('all')
                    return tmp.name, output
            
            return None, output
            
        except Exception as e:
            return None, f"Error executing code:\n{str(e)}"
        finally:
            sys.stdout = old_stdout

def query_api(prompt: str, api_url: str, api_key: str, system_prompt: str) -> str:
    """Send a prompt to the specified API and return the response"""
    headers = {
        "Content-Type": "application/json",
        "Authorization": f"Bearer {api_key}"
    }
    
    payload = {
        "messages": [
            {"role": "system", "content": system_prompt},
            {"role": "user", "content": prompt}
        ]
    }

    try:
        response = requests.post(api_url, headers=headers, json=payload)
        response.raise_for_status()
        return response.json()["choices"][0]["message"]["content"]
    except requests.exceptions.RequestException as e:
        return f"API Error: {str(e)}"

def analyze_data(
    csv_file: str, 
    api_url: str, 
    api_key: str, 
    system_prompt: str
) -> Tuple[str, str, str, Optional[str]]:
    """Analyze uploaded CSV data using the API and execute the generated code"""
    
    if not csv_file:
        return "No file uploaded.", None, None, None

    try:
        # Create safe executor
        executor = SafeExecutor()
        
        # Read the CSV file
        df = pd.read_csv(csv_file.name)
        columns = df.columns.tolist()
        sample_data = df.head(3).to_dict()

        # Build the prompt
        prompt = f"""Analyze this CSV file with columns: {columns}.
Sample data: {sample_data}

Generate Python code that:
1. Creates insightful visualizations using matplotlib or seaborn
2. Performs relevant statistical analysis
3. Identifies key patterns or insights
4. Properly handles potential data issues

Important: Use only these libraries: {', '.join(SAFE_IMPORTS)}"""

        # Get code from API
        generated_code = query_api(prompt, api_url, api_key, system_prompt)
        
        # Create execution environment
        globals_dict = {'df': df, 'pd': pd, 'np': np, 'plt': plt, 'sns': sns}
        
        # Execute the code
        vis_path, execution_output = executor.execute_code(generated_code, globals_dict)
        
        status = "Analysis completed successfully."
        return status, generated_code, execution_output, vis_path

    except Exception as e:
        return f"Error during analysis: {str(e)}", None, None, None

def create_interface():
    """Create the Gradio interface"""
    with gr.Blocks() as interface:
        gr.Markdown("# AI-Powered Data Analysis Tool")
        
        with gr.Row():
            with gr.Column():
                api_url = gr.Textbox(
                    label="API URL",
                    placeholder="Enter API endpoint URL",
                    type="text"
                )
                api_key = gr.Textbox(
                    label="API Key",
                    placeholder="Enter API key",
                    type="password"
                )
                system_prompt = gr.Textbox(
                    label="System Prompt",
                    placeholder="Enter system prompt for the AI",
                    value="You are an AI assistant specialized in data analysis and visualization.",
                    lines=3
                )
                csv_file = gr.File(
                    label="Upload CSV File",
                    file_types=[".csv"]
                )
                analyze_button = gr.Button("Analyze Data")

            with gr.Column():
                status_output = gr.Textbox(label="Status")
                code_output = gr.Code(
                    label="Generated Code",
                    language="python"
                )
                execution_output = gr.Textbox(
                    label="Execution Output",
                    lines=10
                )
                visualization_output = gr.Image(
                    label="Visualization",
                    type="filepath"
                )

        analyze_button.click(
            fn=analyze_data,
            inputs=[csv_file, api_url, api_key, system_prompt],
            outputs=[status_output, code_output, execution_output, visualization_output]
        )

        gr.Markdown("""
        ## How to Use
        1. Enter your API URL and key (supports various API providers)
        2. Customize the system prompt if desired
        3. Upload a CSV file for analysis
        4. Click 'Analyze Data' to generate and execute analysis code
        
        The tool will:
        - Generate Python code to analyze your data
        - Execute the code safely in a controlled environment
        - Display both textual results and visualizations
        - Support common data science libraries
        """)

    return interface

if __name__ == "__main__":
    interface = create_interface()
    interface.launch()