Update app.py
Browse files
app.py
CHANGED
@@ -77,7 +77,7 @@ def safe_exec(code_string: str, local_vars: dict):
|
|
77 |
def load_and_process_file(file_obj, state_dict):
|
78 |
"""Loads a CSV, processes it, and updates the entire UI state."""
|
79 |
if file_obj is None:
|
80 |
-
return state_dict, "Please upload a file.", *[gr.update(visible=False)] *
|
81 |
try:
|
82 |
df = pd.read_csv(file_obj.name, low_memory=False)
|
83 |
for col in df.select_dtypes(include=['object']).columns:
|
@@ -111,7 +111,7 @@ def load_and_process_file(file_obj, state_dict):
|
|
111 |
gr.update(value=f"{len(metadata['datetime_cols'])}"),
|
112 |
gr.update(choices=metadata['columns']), gr.update(choices=metadata['columns']), gr.update(choices=metadata['columns']))
|
113 |
except Exception as e:
|
114 |
-
return state_dict, f"β **Error:** {e}", *[gr.update()] *
|
115 |
|
116 |
def extract_dataset_metadata(df: pd.DataFrame):
|
117 |
rows, cols = df.shape
|
@@ -160,11 +160,23 @@ def get_ai_suggestions(state_dict, api_key):
|
|
160 |
suggestions = json.loads(response.text)
|
161 |
|
162 |
# Create a button for each suggestion
|
163 |
-
buttons = [gr.Button(s, variant="secondary") for s in suggestions]
|
164 |
-
|
|
|
|
|
|
|
165 |
|
166 |
except Exception as e:
|
167 |
-
return f"Could not generate suggestions: {e}", gr.update(visible=False)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
168 |
|
169 |
# --- Page 2: Deep Dive Dashboard ---
|
170 |
def add_plot_to_dashboard(state_dict, x_col, y_col, plot_type):
|
@@ -261,7 +273,7 @@ def respond_to_chat(user_message, history, state_dict, api_key):
|
|
261 |
if df_result is not None: output_updates[3] = gr.update(visible=True, value=df_result)
|
262 |
if stdout:
|
263 |
# Append stdout to explanation if it exists
|
264 |
-
new_explanation = (output_updates[0]['value'] if output_updates[0]['
|
265 |
output_updates[0] = gr.update(visible=True, value=new_explanation)
|
266 |
if error:
|
267 |
error_explanation = f"**Phoenix Co-pilot:** I encountered an error. Here's the details:\n\n`{error}`"
|
@@ -304,7 +316,8 @@ def create_gradio_interface():
|
|
304 |
with gr.Column(visible=True) as welcome_page:
|
305 |
gr.Markdown("# Welcome to the AI Data Explorer (Phoenix UI)", elem_id="welcome-header")
|
306 |
gr.Markdown("Please **upload a CSV file** and **enter your Gemini API key** in the sidebar to begin.")
|
307 |
-
|
|
|
308 |
|
309 |
# Page 1: Data Cockpit (Hidden initially)
|
310 |
with gr.Column(visible=False) as cockpit_page:
|
@@ -324,7 +337,8 @@ def create_gradio_interface():
|
|
324 |
time_cols_stat = gr.Textbox("0", show_label=False, elem_classes="stat-card-value")
|
325 |
|
326 |
suggestion_status = gr.Markdown(visible=True)
|
327 |
-
|
|
|
328 |
|
329 |
# Page 2: Deep Dive Dashboard Builder (Hidden initially)
|
330 |
with gr.Column(visible=False) as deep_dive_page:
|
@@ -343,9 +357,8 @@ def create_gradio_interface():
|
|
343 |
with gr.Column(visible=False) as copilot_page:
|
344 |
gr.Markdown("## π€ AI Co-pilot")
|
345 |
gr.Markdown("Ask complex questions in natural language. The Co-pilot will write and execute code to find the answer.")
|
346 |
-
chatbot = gr.Chatbot(height=400, label="Conversation with Co-pilot")
|
347 |
|
348 |
-
# AI's multi-modal response area
|
349 |
with gr.Accordion("Co-pilot's Response Details", open=True):
|
350 |
copilot_explanation = gr.Markdown(visible=False, elem_classes="explanation-block")
|
351 |
copilot_code = gr.Code(language="python", visible=False, label="Executed Python Code", elem_classes="code-block")
|
@@ -357,55 +370,49 @@ def create_gradio_interface():
|
|
357 |
chat_submit_btn = gr.Button("Submit", variant="primary")
|
358 |
|
359 |
# --- Event Handlers ---
|
360 |
-
# Page Navigation
|
361 |
pages = [cockpit_page, deep_dive_page, copilot_page]
|
362 |
nav_buttons = [cockpit_btn, deep_dive_btn, copilot_btn]
|
363 |
|
364 |
for i, btn in enumerate(nav_buttons):
|
365 |
-
btn.click(
|
366 |
-
|
367 |
-
|
368 |
-
|
|
|
|
|
|
|
369 |
|
370 |
-
# File Upload
|
371 |
file_input.upload(
|
372 |
fn=load_and_process_file,
|
373 |
inputs=[file_input, global_state],
|
374 |
outputs=[global_state, status_output, welcome_page, cockpit_page, deep_dive_page, copilot_page,
|
375 |
rows_stat, cols_stat, quality_stat, time_cols_stat,
|
376 |
-
x_col_dd, y_col_dd,
|
|
|
|
|
377 |
).then(
|
378 |
-
|
379 |
-
outputs=nav_buttons
|
380 |
)
|
381 |
|
382 |
-
# Suggestions Button
|
383 |
suggestion_btn.click(
|
384 |
get_ai_suggestions,
|
385 |
[global_state, api_key_input],
|
386 |
-
[suggestion_status,
|
387 |
-
).then(
|
388 |
-
fn=lambda: [gr.Button.update(visible=True) for _ in range(5)], # Assumes max 5 suggestions for demo
|
389 |
-
outputs=[b for b in suggestion_accordion.children] if isinstance(suggestion_accordion, gr.Accordion) and suggestion_accordion.children else []
|
390 |
)
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
|
402 |
-
|
403 |
-
# Dashboard Builder
|
404 |
add_plot_btn.click(add_plot_to_dashboard, [global_state, x_col_dd, y_col_dd, plot_type_dd], [global_state, dashboard_accordion])
|
405 |
clear_plots_btn.click(clear_dashboard, [global_state], [global_state, dashboard_accordion])
|
406 |
|
407 |
-
# AI Co-pilot Chat
|
408 |
-
copilot_input_col_ref_for_dynamic_update = x_col_dd # Dummy placeholder for dynamic updates
|
409 |
chat_submit_btn.click(
|
410 |
respond_to_chat,
|
411 |
[chat_input, chatbot, global_state, api_key_input],
|
|
|
77 |
def load_and_process_file(file_obj, state_dict):
|
78 |
"""Loads a CSV, processes it, and updates the entire UI state."""
|
79 |
if file_obj is None:
|
80 |
+
return state_dict, "Please upload a file.", *[gr.update(visible=False)] * 4
|
81 |
try:
|
82 |
df = pd.read_csv(file_obj.name, low_memory=False)
|
83 |
for col in df.select_dtypes(include=['object']).columns:
|
|
|
111 |
gr.update(value=f"{len(metadata['datetime_cols'])}"),
|
112 |
gr.update(choices=metadata['columns']), gr.update(choices=metadata['columns']), gr.update(choices=metadata['columns']))
|
113 |
except Exception as e:
|
114 |
+
return state_dict, f"β **Error:** {e}", *[gr.update()] * 11
|
115 |
|
116 |
def extract_dataset_metadata(df: pd.DataFrame):
|
117 |
rows, cols = df.shape
|
|
|
160 |
suggestions = json.loads(response.text)
|
161 |
|
162 |
# Create a button for each suggestion
|
163 |
+
buttons = [gr.Button(s, variant="secondary", visible=True) for s in suggestions]
|
164 |
+
# Pad with hidden buttons to always have 5 outputs
|
165 |
+
buttons += [gr.Button(visible=False)] * (5 - len(buttons))
|
166 |
+
|
167 |
+
return gr.update(visible=False), *buttons
|
168 |
|
169 |
except Exception as e:
|
170 |
+
return f"Could not generate suggestions: {e}", *[gr.update(visible=False)]*5
|
171 |
+
|
172 |
+
def handle_suggestion_click(question_text):
|
173 |
+
"""When a suggestion button is clicked, switch to the co-pilot page and populate the input."""
|
174 |
+
return (
|
175 |
+
gr.update(visible=False), # Hide cockpit
|
176 |
+
gr.update(visible=False), # Hide deep dive
|
177 |
+
gr.update(visible=True), # Show co-pilot
|
178 |
+
question_text # Populate the chat input
|
179 |
+
)
|
180 |
|
181 |
# --- Page 2: Deep Dive Dashboard ---
|
182 |
def add_plot_to_dashboard(state_dict, x_col, y_col, plot_type):
|
|
|
273 |
if df_result is not None: output_updates[3] = gr.update(visible=True, value=df_result)
|
274 |
if stdout:
|
275 |
# Append stdout to explanation if it exists
|
276 |
+
new_explanation = (output_updates[0]['value'] if output_updates[0]['visible'] else "") + f"\n\n**Console Output:**\n```\n{stdout}\n```"
|
277 |
output_updates[0] = gr.update(visible=True, value=new_explanation)
|
278 |
if error:
|
279 |
error_explanation = f"**Phoenix Co-pilot:** I encountered an error. Here's the details:\n\n`{error}`"
|
|
|
316 |
with gr.Column(visible=True) as welcome_page:
|
317 |
gr.Markdown("# Welcome to the AI Data Explorer (Phoenix UI)", elem_id="welcome-header")
|
318 |
gr.Markdown("Please **upload a CSV file** and **enter your Gemini API key** in the sidebar to begin.")
|
319 |
+
# CORRECTED: Uses a local file, 'workflow.png', which must be in the same directory.
|
320 |
+
gr.Image(value="workflow.png", label="Workflow", show_label=False, show_download_button=False, container=False)
|
321 |
|
322 |
# Page 1: Data Cockpit (Hidden initially)
|
323 |
with gr.Column(visible=False) as cockpit_page:
|
|
|
337 |
time_cols_stat = gr.Textbox("0", show_label=False, elem_classes="stat-card-value")
|
338 |
|
339 |
suggestion_status = gr.Markdown(visible=True)
|
340 |
+
with gr.Accordion(label="β¨ AI Smart Suggestions", open=True):
|
341 |
+
suggestion_buttons = [gr.Button(visible=False) for _ in range(5)]
|
342 |
|
343 |
# Page 2: Deep Dive Dashboard Builder (Hidden initially)
|
344 |
with gr.Column(visible=False) as deep_dive_page:
|
|
|
357 |
with gr.Column(visible=False) as copilot_page:
|
358 |
gr.Markdown("## π€ AI Co-pilot")
|
359 |
gr.Markdown("Ask complex questions in natural language. The Co-pilot will write and execute code to find the answer.")
|
360 |
+
chatbot = gr.Chatbot(height=400, label="Conversation with Co-pilot", show_copy_button=True)
|
361 |
|
|
|
362 |
with gr.Accordion("Co-pilot's Response Details", open=True):
|
363 |
copilot_explanation = gr.Markdown(visible=False, elem_classes="explanation-block")
|
364 |
copilot_code = gr.Code(language="python", visible=False, label="Executed Python Code", elem_classes="code-block")
|
|
|
370 |
chat_submit_btn = gr.Button("Submit", variant="primary")
|
371 |
|
372 |
# --- Event Handlers ---
|
|
|
373 |
pages = [cockpit_page, deep_dive_page, copilot_page]
|
374 |
nav_buttons = [cockpit_btn, deep_dive_btn, copilot_btn]
|
375 |
|
376 |
for i, btn in enumerate(nav_buttons):
|
377 |
+
btn.click(
|
378 |
+
lambda i=i: (gr.update(visible=i==0), gr.update(visible=i==1), gr.update(visible=i==2)),
|
379 |
+
outputs=pages
|
380 |
+
).then(
|
381 |
+
lambda i=i: [gr.update(elem_classes="selected" if j==i else "") for j in range(len(nav_buttons))],
|
382 |
+
outputs=nav_buttons
|
383 |
+
)
|
384 |
|
|
|
385 |
file_input.upload(
|
386 |
fn=load_and_process_file,
|
387 |
inputs=[file_input, global_state],
|
388 |
outputs=[global_state, status_output, welcome_page, cockpit_page, deep_dive_page, copilot_page,
|
389 |
rows_stat, cols_stat, quality_stat, time_cols_stat,
|
390 |
+
x_col_dd, y_col_dd, plot_type_dd]
|
391 |
+
).then(
|
392 |
+
lambda: (gr.update(visible=True), gr.update(visible=False), gr.update(visible=False)), outputs=pages
|
393 |
).then(
|
394 |
+
lambda: (gr.update(elem_classes="selected"), gr.update(elem_classes=""), gr.update(elem_classes="")), outputs=nav_buttons
|
|
|
395 |
)
|
396 |
|
|
|
397 |
suggestion_btn.click(
|
398 |
get_ai_suggestions,
|
399 |
[global_state, api_key_input],
|
400 |
+
[suggestion_status, *suggestion_buttons]
|
|
|
|
|
|
|
401 |
)
|
402 |
+
|
403 |
+
for btn in suggestion_buttons:
|
404 |
+
btn.click(
|
405 |
+
handle_suggestion_click,
|
406 |
+
inputs=[btn],
|
407 |
+
outputs=[cockpit_page, deep_dive_page, copilot_page, chat_input]
|
408 |
+
).then(
|
409 |
+
lambda: (gr.update(elem_classes=""), gr.update(elem_classes=""), gr.update(elem_classes="selected")),
|
410 |
+
outputs=nav_buttons
|
411 |
+
)
|
412 |
+
|
|
|
|
|
413 |
add_plot_btn.click(add_plot_to_dashboard, [global_state, x_col_dd, y_col_dd, plot_type_dd], [global_state, dashboard_accordion])
|
414 |
clear_plots_btn.click(clear_dashboard, [global_state], [global_state, dashboard_accordion])
|
415 |
|
|
|
|
|
416 |
chat_submit_btn.click(
|
417 |
respond_to_chat,
|
418 |
[chat_input, chatbot, global_state, api_key_input],
|