Jeremy Live
commited on
Commit
·
707e65a
1
Parent(s):
cf30c20
upgrade fix chatgpt5
Browse files
app.py
CHANGED
@@ -43,21 +43,21 @@ def generate_chart(data: Union[Dict, List[Dict], pd.DataFrame],
|
|
43 |
y: str = None,
|
44 |
title: str = "",
|
45 |
x_label: str = None,
|
46 |
-
y_label: str = None)
|
47 |
"""
|
48 |
-
Generate
|
49 |
|
50 |
Args:
|
51 |
data: The data to plot (can be a list of dicts or a pandas DataFrame)
|
52 |
chart_type: Type of chart to generate (bar, line, pie, scatter, histogram)
|
53 |
-
x: Column name for x-axis
|
54 |
-
y: Column name for y-axis (
|
55 |
title: Chart title
|
56 |
x_label: Label for x-axis
|
57 |
y_label: Label for y-axis
|
58 |
|
59 |
Returns:
|
60 |
-
|
61 |
"""
|
62 |
try:
|
63 |
# Convert data to DataFrame if it's a list of dicts
|
@@ -69,7 +69,7 @@ def generate_chart(data: Union[Dict, List[Dict], pd.DataFrame],
|
|
69 |
df = data
|
70 |
|
71 |
if not isinstance(df, pd.DataFrame):
|
72 |
-
return
|
73 |
|
74 |
# Generate the appropriate chart type
|
75 |
fig = None
|
@@ -78,13 +78,13 @@ def generate_chart(data: Union[Dict, List[Dict], pd.DataFrame],
|
|
78 |
elif chart_type == 'line':
|
79 |
fig = px.line(df, x=x, y=y, title=title)
|
80 |
elif chart_type == 'pie':
|
81 |
-
fig = px.pie(df, names=x, values=y, title=title)
|
82 |
elif chart_type == 'scatter':
|
83 |
fig = px.scatter(df, x=x, y=y, title=title)
|
84 |
elif chart_type == 'histogram':
|
85 |
fig = px.histogram(df, x=x, title=title)
|
86 |
else:
|
87 |
-
return
|
88 |
|
89 |
# Update layout
|
90 |
fig.update_layout(
|
@@ -96,24 +96,13 @@ def generate_chart(data: Union[Dict, List[Dict], pd.DataFrame],
|
|
96 |
height=400
|
97 |
)
|
98 |
|
99 |
-
|
100 |
-
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.png')
|
101 |
-
fig.write_image(temp_file.name, format='png', engine='kaleido')
|
102 |
-
|
103 |
-
# Read the image file and encode as base64
|
104 |
-
with open(temp_file.name, 'rb') as img_file:
|
105 |
-
img_base64 = base64.b64encode(img_file.read()).decode('utf-8')
|
106 |
-
|
107 |
-
# Clean up the temporary file
|
108 |
-
os.unlink(temp_file.name)
|
109 |
-
|
110 |
-
# Return as markdown image
|
111 |
-
return f'<img src="data:image/png;base64,{img_base64}" style="max-width:100%;"/>'
|
112 |
|
113 |
except Exception as e:
|
114 |
error_msg = f"Error generating chart: {str(e)}"
|
115 |
logger.error(error_msg, exc_info=True)
|
116 |
-
return
|
|
|
117 |
logger = logging.getLogger(__name__)
|
118 |
|
119 |
def check_environment():
|
@@ -423,12 +412,13 @@ def convert_to_messages_format(chat_history):
|
|
423 |
|
424 |
return messages
|
425 |
|
426 |
-
async def stream_agent_response(question: str, chat_history: List[List[str]]) -> str:
|
427 |
"""Procesa la pregunta del usuario y devuelve la respuesta del agente con memoria de conversación."""
|
428 |
global agent # Make sure we can modify the agent's memory
|
429 |
|
430 |
# Initialize response
|
431 |
response_text = ""
|
|
|
432 |
messages = []
|
433 |
|
434 |
# Add previous chat history in the correct format for the agent
|
@@ -505,7 +495,7 @@ async def stream_agent_response(question: str, chat_history: List[List[str]]) ->
|
|
505 |
# Add the query and its result to the response
|
506 |
response_text += f"\n\n### 🔍 Resultado de la consulta:\n```sql\n{sql_query}\n```\n\n{query_result}"
|
507 |
|
508 |
-
# Try to generate
|
509 |
try:
|
510 |
if isinstance(query_result, str) and '|' in query_result and '---' in query_result:
|
511 |
# Convert markdown table to DataFrame
|
@@ -526,32 +516,37 @@ async def stream_agent_response(question: str, chat_history: List[List[str]]) ->
|
|
526 |
data.append(dict(zip(columns, values)))
|
527 |
|
528 |
if data and len(columns) >= 2:
|
529 |
-
#
|
530 |
-
|
531 |
-
if
|
532 |
-
|
533 |
-
|
534 |
-
|
535 |
-
|
536 |
-
|
537 |
-
|
538 |
-
|
539 |
-
|
540 |
-
|
541 |
-
|
542 |
-
|
543 |
-
|
544 |
-
|
545 |
-
|
546 |
-
|
547 |
-
|
548 |
-
|
549 |
-
|
550 |
-
|
551 |
-
|
552 |
-
|
553 |
-
|
554 |
-
|
|
|
|
|
|
|
|
|
|
|
555 |
except Exception as e:
|
556 |
logger.error(f"Error generating chart: {str(e)}", exc_info=True)
|
557 |
# Don't fail the whole request if chart generation fails
|
@@ -579,15 +574,14 @@ async def stream_agent_response(question: str, chat_history: List[List[str]]) ->
|
|
579 |
else:
|
580 |
message_content = str(assistant_message)
|
581 |
|
582 |
-
# Return the assistant's response
|
583 |
-
|
584 |
-
return message_content
|
585 |
|
586 |
except Exception as e:
|
587 |
error_msg = f"## ❌ Error\n\nOcurrió un error al procesar tu solicitud:\n\n```\n{str(e)}\n```"
|
588 |
logger.error(f"Error in stream_agent_response: {str(e)}", exc_info=True)
|
589 |
-
#
|
590 |
-
return
|
591 |
|
592 |
# Custom CSS for the app
|
593 |
custom_css = """
|
@@ -703,6 +697,13 @@ def create_ui():
|
|
703 |
layout="panel" # Better layout for messages
|
704 |
)
|
705 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
706 |
# Input area
|
707 |
with gr.Row():
|
708 |
question_input = gr.Textbox(
|
@@ -788,12 +789,12 @@ def create_ui():
|
|
788 |
# Hidden component for streaming output
|
789 |
streaming_output_display = gr.Textbox(visible=False)
|
790 |
|
791 |
-
return demo, chatbot, question_input, submit_button, streaming_output_display
|
792 |
|
793 |
def create_application():
|
794 |
"""Create and configure the Gradio application."""
|
795 |
# Create the UI components
|
796 |
-
demo, chatbot, question_input, submit_button, streaming_output_display = create_ui()
|
797 |
|
798 |
def user_message(user_input: str, chat_history: List[List[str]]) -> Tuple[str, List[List[str]]]:
|
799 |
"""Add user message to chat history and clear input."""
|
@@ -812,34 +813,34 @@ def create_application():
|
|
812 |
# Clear the input
|
813 |
return "", chat_history
|
814 |
|
815 |
-
async def bot_response(chat_history: List[List[str]]) -> List[List[str]]:
|
816 |
-
"""Get bot response and update chat history."""
|
817 |
if not chat_history:
|
818 |
-
return chat_history
|
819 |
|
820 |
# Get the last user message (first element of the last list if it exists)
|
821 |
if not chat_history[-1][0] or chat_history[-1][1] is not None:
|
822 |
-
return chat_history
|
823 |
|
824 |
try:
|
825 |
question = chat_history[-1][0]
|
826 |
logger.info(f"Processing question: {question}")
|
827 |
|
828 |
# Call the agent and get the response
|
829 |
-
assistant_message = await stream_agent_response(question, chat_history[:-1])
|
830 |
|
831 |
# Update the assistant's message in the chat history
|
832 |
chat_history[-1] = [question, assistant_message]
|
833 |
|
834 |
logger.info("Response generation complete")
|
835 |
-
return chat_history
|
836 |
|
837 |
except Exception as e:
|
838 |
error_msg = f"## ❌ Error\n\nError al procesar la solicitud:\n\n```\n{str(e)}\n```"
|
839 |
logger.error(error_msg, exc_info=True)
|
840 |
if chat_history and len(chat_history[-1]) == 2 and chat_history[-1][1] is None:
|
841 |
chat_history[-1] = [chat_history[-1][0], error_msg]
|
842 |
-
return chat_history
|
843 |
|
844 |
# Event handlers
|
845 |
with demo:
|
@@ -852,7 +853,7 @@ def create_application():
|
|
852 |
).then(
|
853 |
fn=bot_response,
|
854 |
inputs=[chatbot],
|
855 |
-
outputs=[chatbot],
|
856 |
api_name="ask"
|
857 |
)
|
858 |
|
@@ -865,7 +866,7 @@ def create_application():
|
|
865 |
).then(
|
866 |
fn=bot_response,
|
867 |
inputs=[chatbot],
|
868 |
-
outputs=[chatbot]
|
869 |
)
|
870 |
|
871 |
return demo
|
|
|
43 |
y: str = None,
|
44 |
title: str = "",
|
45 |
x_label: str = None,
|
46 |
+
y_label: str = None):
|
47 |
"""
|
48 |
+
Generate an interactive Plotly figure from data.
|
49 |
|
50 |
Args:
|
51 |
data: The data to plot (can be a list of dicts or a pandas DataFrame)
|
52 |
chart_type: Type of chart to generate (bar, line, pie, scatter, histogram)
|
53 |
+
x: Column name for x-axis (names for pie)
|
54 |
+
y: Column name for y-axis (values for pie)
|
55 |
title: Chart title
|
56 |
x_label: Label for x-axis
|
57 |
y_label: Label for y-axis
|
58 |
|
59 |
Returns:
|
60 |
+
A Plotly Figure object (interactive) or None on error
|
61 |
"""
|
62 |
try:
|
63 |
# Convert data to DataFrame if it's a list of dicts
|
|
|
69 |
df = data
|
70 |
|
71 |
if not isinstance(df, pd.DataFrame):
|
72 |
+
return None
|
73 |
|
74 |
# Generate the appropriate chart type
|
75 |
fig = None
|
|
|
78 |
elif chart_type == 'line':
|
79 |
fig = px.line(df, x=x, y=y, title=title)
|
80 |
elif chart_type == 'pie':
|
81 |
+
fig = px.pie(df, names=x, values=y, title=title, hole=0)
|
82 |
elif chart_type == 'scatter':
|
83 |
fig = px.scatter(df, x=x, y=y, title=title)
|
84 |
elif chart_type == 'histogram':
|
85 |
fig = px.histogram(df, x=x, title=title)
|
86 |
else:
|
87 |
+
return None
|
88 |
|
89 |
# Update layout
|
90 |
fig.update_layout(
|
|
|
96 |
height=400
|
97 |
)
|
98 |
|
99 |
+
return fig
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
100 |
|
101 |
except Exception as e:
|
102 |
error_msg = f"Error generating chart: {str(e)}"
|
103 |
logger.error(error_msg, exc_info=True)
|
104 |
+
return None
|
105 |
+
|
106 |
logger = logging.getLogger(__name__)
|
107 |
|
108 |
def check_environment():
|
|
|
412 |
|
413 |
return messages
|
414 |
|
415 |
+
async def stream_agent_response(question: str, chat_history: List[List[str]]) -> Tuple[str, Optional["go.Figure"]]:
|
416 |
"""Procesa la pregunta del usuario y devuelve la respuesta del agente con memoria de conversación."""
|
417 |
global agent # Make sure we can modify the agent's memory
|
418 |
|
419 |
# Initialize response
|
420 |
response_text = ""
|
421 |
+
chart_fig = None
|
422 |
messages = []
|
423 |
|
424 |
# Add previous chat history in the correct format for the agent
|
|
|
495 |
# Add the query and its result to the response
|
496 |
response_text += f"\n\n### 🔍 Resultado de la consulta:\n```sql\n{sql_query}\n```\n\n{query_result}"
|
497 |
|
498 |
+
# Try to generate an interactive chart if the result is tabular
|
499 |
try:
|
500 |
if isinstance(query_result, str) and '|' in query_result and '---' in query_result:
|
501 |
# Convert markdown table to DataFrame
|
|
|
516 |
data.append(dict(zip(columns, values)))
|
517 |
|
518 |
if data and len(columns) >= 2:
|
519 |
+
# Determine chart type from user's question (supports pie chart)
|
520 |
+
q_lower = question.lower()
|
521 |
+
if any(k in q_lower for k in ["gráfico circular", "grafico circular", "pie", "pastel"]):
|
522 |
+
desired_type = 'pie'
|
523 |
+
elif any(k in q_lower for k in ["línea", "linea", "line"]):
|
524 |
+
desired_type = 'line'
|
525 |
+
elif any(k in q_lower for k in ["dispersión", "dispersion", "scatter"]):
|
526 |
+
desired_type = 'scatter'
|
527 |
+
elif any(k in q_lower for k in ["histograma", "histogram"]):
|
528 |
+
desired_type = 'histogram'
|
529 |
+
else:
|
530 |
+
desired_type = 'bar'
|
531 |
+
|
532 |
+
# Choose x/y columns (assume first is category, second numeric)
|
533 |
+
x_col = columns[0]
|
534 |
+
y_col = columns[1]
|
535 |
+
|
536 |
+
# Coerce numeric values for y
|
537 |
+
for row in data:
|
538 |
+
try:
|
539 |
+
row[y_col] = float(re.sub(r"[^0-9.\-]", "", str(row[y_col])))
|
540 |
+
except Exception:
|
541 |
+
pass
|
542 |
+
|
543 |
+
chart_fig = generate_chart(
|
544 |
+
data=data,
|
545 |
+
chart_type=desired_type,
|
546 |
+
x=x_col,
|
547 |
+
y=y_col,
|
548 |
+
title=f"{y_col} por {x_col}"
|
549 |
+
)
|
550 |
except Exception as e:
|
551 |
logger.error(f"Error generating chart: {str(e)}", exc_info=True)
|
552 |
# Don't fail the whole request if chart generation fails
|
|
|
574 |
else:
|
575 |
message_content = str(assistant_message)
|
576 |
|
577 |
+
# Return the assistant's response and an optional interactive chart figure
|
578 |
+
return message_content, chart_fig
|
|
|
579 |
|
580 |
except Exception as e:
|
581 |
error_msg = f"## ❌ Error\n\nOcurrió un error al procesar tu solicitud:\n\n```\n{str(e)}\n```"
|
582 |
logger.error(f"Error in stream_agent_response: {str(e)}", exc_info=True)
|
583 |
+
# Return error message and no chart
|
584 |
+
return error_msg, None
|
585 |
|
586 |
# Custom CSS for the app
|
587 |
custom_css = """
|
|
|
697 |
layout="panel" # Better layout for messages
|
698 |
)
|
699 |
|
700 |
+
# Chart display area (interactive Plotly figure)
|
701 |
+
chart_display = gr.Plot(
|
702 |
+
label="📊 Visualización",
|
703 |
+
height=420,
|
704 |
+
interactive=True,
|
705 |
+
)
|
706 |
+
|
707 |
# Input area
|
708 |
with gr.Row():
|
709 |
question_input = gr.Textbox(
|
|
|
789 |
# Hidden component for streaming output
|
790 |
streaming_output_display = gr.Textbox(visible=False)
|
791 |
|
792 |
+
return demo, chatbot, chart_display, question_input, submit_button, streaming_output_display
|
793 |
|
794 |
def create_application():
|
795 |
"""Create and configure the Gradio application."""
|
796 |
# Create the UI components
|
797 |
+
demo, chatbot, chart_display, question_input, submit_button, streaming_output_display = create_ui()
|
798 |
|
799 |
def user_message(user_input: str, chat_history: List[List[str]]) -> Tuple[str, List[List[str]]]:
|
800 |
"""Add user message to chat history and clear input."""
|
|
|
813 |
# Clear the input
|
814 |
return "", chat_history
|
815 |
|
816 |
+
async def bot_response(chat_history: List[List[str]]) -> Tuple[List[List[str]], Optional[go.Figure]]:
|
817 |
+
"""Get bot response and update chat history and return optional chart figure."""
|
818 |
if not chat_history:
|
819 |
+
return chat_history, None
|
820 |
|
821 |
# Get the last user message (first element of the last list if it exists)
|
822 |
if not chat_history[-1][0] or chat_history[-1][1] is not None:
|
823 |
+
return chat_history, None
|
824 |
|
825 |
try:
|
826 |
question = chat_history[-1][0]
|
827 |
logger.info(f"Processing question: {question}")
|
828 |
|
829 |
# Call the agent and get the response
|
830 |
+
assistant_message, chart_fig = await stream_agent_response(question, chat_history[:-1])
|
831 |
|
832 |
# Update the assistant's message in the chat history
|
833 |
chat_history[-1] = [question, assistant_message]
|
834 |
|
835 |
logger.info("Response generation complete")
|
836 |
+
return chat_history, chart_fig
|
837 |
|
838 |
except Exception as e:
|
839 |
error_msg = f"## ❌ Error\n\nError al procesar la solicitud:\n\n```\n{str(e)}\n```"
|
840 |
logger.error(error_msg, exc_info=True)
|
841 |
if chat_history and len(chat_history[-1]) == 2 and chat_history[-1][1] is None:
|
842 |
chat_history[-1] = [chat_history[-1][0], error_msg]
|
843 |
+
return chat_history, None
|
844 |
|
845 |
# Event handlers
|
846 |
with demo:
|
|
|
853 |
).then(
|
854 |
fn=bot_response,
|
855 |
inputs=[chatbot],
|
856 |
+
outputs=[chatbot, chart_display],
|
857 |
api_name="ask"
|
858 |
)
|
859 |
|
|
|
866 |
).then(
|
867 |
fn=bot_response,
|
868 |
inputs=[chatbot],
|
869 |
+
outputs=[chatbot, chart_display]
|
870 |
)
|
871 |
|
872 |
return demo
|