Jeremy Live
commited on
Commit
·
f2961b7
1
Parent(s):
8bb4446
mm2
Browse files
app.py
CHANGED
@@ -417,6 +417,40 @@ def execute_sql_query(query, db_connection):
|
|
417 |
except Exception as e:
|
418 |
return f"Error ejecutando la consulta: {str(e)}"
|
419 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
420 |
def generate_plot(data, x_col, y_col, title, x_label, y_label):
|
421 |
"""Generate a plot from data and return the file path."""
|
422 |
plt.figure(figsize=(10, 6))
|
@@ -563,18 +597,8 @@ async def stream_agent_response(question: str, chat_history: List[List[str]]) ->
|
|
563 |
data.append(dict(zip(columns, values)))
|
564 |
|
565 |
if data and len(columns) >= 2:
|
566 |
-
# Determine chart type from user's question
|
567 |
-
|
568 |
-
if any(k in q_lower for k in ["gráfico circular", "grafico circular", "pie", "pastel"]):
|
569 |
-
desired_type = 'pie'
|
570 |
-
elif any(k in q_lower for k in ["línea", "linea", "line"]):
|
571 |
-
desired_type = 'line'
|
572 |
-
elif any(k in q_lower for k in ["dispersión", "dispersion", "scatter"]):
|
573 |
-
desired_type = 'scatter'
|
574 |
-
elif any(k in q_lower for k in ["histograma", "histogram"]):
|
575 |
-
desired_type = 'histogram'
|
576 |
-
else:
|
577 |
-
desired_type = 'bar'
|
578 |
|
579 |
# Choose x/y columns (assume first is category, second numeric)
|
580 |
x_col = columns[0]
|
@@ -608,8 +632,7 @@ async def stream_agent_response(question: str, chat_history: List[List[str]]) ->
|
|
608 |
# If we still have no chart but the user clearly wants one,
|
609 |
# try a second pass to get ONLY a SQL query from the agent and execute it.
|
610 |
if chart_fig is None:
|
611 |
-
|
612 |
-
wants_chart = any(k in q_lower for k in ["gráfico", "grafico", "chart", "graph", "pastel", "pie"])
|
613 |
if wants_chart:
|
614 |
try:
|
615 |
logger.info("Second pass: asking agent for ONLY SQL query in fenced block.")
|
@@ -648,7 +671,7 @@ async def stream_agent_response(question: str, chat_history: List[List[str]]) ->
|
|
648 |
except Exception:
|
649 |
continue
|
650 |
if y_col:
|
651 |
-
desired_type =
|
652 |
chart_fig = generate_chart(
|
653 |
data=data,
|
654 |
chart_type=desired_type,
|
@@ -663,11 +686,10 @@ async def stream_agent_response(question: str, chat_history: List[List[str]]) ->
|
|
663 |
except Exception as e:
|
664 |
logger.error(f"Second-pass SQL synthesis failed: {e}")
|
665 |
|
666 |
-
# Fallback: if user asked for a chart
|
667 |
# parse the most recent assistant text for lines like "LABEL: NUMBER" (bulleted or plain).
|
668 |
if chart_fig is None:
|
669 |
-
|
670 |
-
wants_chart = any(k in q_lower for k in ["gráfico", "grafico", "chart", "graph", "pastel", "pie"])
|
671 |
if wants_chart:
|
672 |
# Find the most recent assistant message with usable numeric pairs
|
673 |
candidate_text = ""
|
@@ -705,7 +727,6 @@ async def stream_agent_response(question: str, chat_history: List[List[str]]) ->
|
|
705 |
data.append({"label": label, "value": val})
|
706 |
logger.info(f"Fallback parse from text: extracted {len(data)} items for potential chart")
|
707 |
if len(data) >= 2:
|
708 |
-
desired_type = 'pie' if any(k in q_lower for k in ["gráfico circular", "grafico circular", "pie", "pastel"]) else 'bar'
|
709 |
chart_fig = generate_chart(
|
710 |
data=data,
|
711 |
chart_type=desired_type,
|
@@ -1013,6 +1034,37 @@ def create_application():
|
|
1013 |
# Append assistant message back into messages history
|
1014 |
chat_history.append({"role": "assistant", "content": assistant_message})
|
1015 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1016 |
logger.info("Response generation complete")
|
1017 |
return chat_history, chart_fig
|
1018 |
|
|
|
417 |
except Exception as e:
|
418 |
return f"Error ejecutando la consulta: {str(e)}"
|
419 |
|
420 |
+
def detect_chart_preferences(question: str) -> Tuple[bool, str]:
|
421 |
+
"""Detect whether the user is asking for a chart and infer desired type.
|
422 |
+
|
423 |
+
Returns (wants_chart, chart_type) where chart_type is one of
|
424 |
+
{'bar', 'pie', 'line', 'scatter', 'histogram'}.
|
425 |
+
Defaults to 'bar' when ambiguous.
|
426 |
+
"""
|
427 |
+
try:
|
428 |
+
q = (question or "").lower()
|
429 |
+
|
430 |
+
# Broad triggers indicating any chart request
|
431 |
+
chart_triggers = [
|
432 |
+
"grafico", "gráfico", "grafica", "gráfica", "chart", "graph",
|
433 |
+
"visualizacion", "visualización", "plot", "plotly", "diagrama"
|
434 |
+
]
|
435 |
+
wants_chart = any(k in q for k in chart_triggers)
|
436 |
+
|
437 |
+
# Specific type hints
|
438 |
+
if any(k in q for k in ["pastel", "pie", "circular", "donut", "dona", "anillo"]):
|
439 |
+
return wants_chart or True, "pie"
|
440 |
+
if any(k in q for k in ["linea", "línea", "line", "tendencia"]):
|
441 |
+
return wants_chart or True, "line"
|
442 |
+
if any(k in q for k in ["dispersión", "dispersion", "scatter", "puntos"]):
|
443 |
+
return wants_chart or True, "scatter"
|
444 |
+
if any(k in q for k in ["histograma", "histogram"]):
|
445 |
+
return wants_chart or True, "histogram"
|
446 |
+
if any(k in q for k in ["barra", "barras", "columnas", "column"]):
|
447 |
+
return wants_chart or True, "bar"
|
448 |
+
|
449 |
+
# Default
|
450 |
+
return wants_chart, "bar"
|
451 |
+
except Exception:
|
452 |
+
return False, "bar"
|
453 |
+
|
454 |
def generate_plot(data, x_col, y_col, title, x_label, y_label):
|
455 |
"""Generate a plot from data and return the file path."""
|
456 |
plt.figure(figsize=(10, 6))
|
|
|
597 |
data.append(dict(zip(columns, values)))
|
598 |
|
599 |
if data and len(columns) >= 2:
|
600 |
+
# Determine chart type from user's question
|
601 |
+
_, desired_type = detect_chart_preferences(question)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
602 |
|
603 |
# Choose x/y columns (assume first is category, second numeric)
|
604 |
x_col = columns[0]
|
|
|
632 |
# If we still have no chart but the user clearly wants one,
|
633 |
# try a second pass to get ONLY a SQL query from the agent and execute it.
|
634 |
if chart_fig is None:
|
635 |
+
wants_chart, default_type = detect_chart_preferences(question)
|
|
|
636 |
if wants_chart:
|
637 |
try:
|
638 |
logger.info("Second pass: asking agent for ONLY SQL query in fenced block.")
|
|
|
671 |
except Exception:
|
672 |
continue
|
673 |
if y_col:
|
674 |
+
desired_type = default_type
|
675 |
chart_fig = generate_chart(
|
676 |
data=data,
|
677 |
chart_type=desired_type,
|
|
|
686 |
except Exception as e:
|
687 |
logger.error(f"Second-pass SQL synthesis failed: {e}")
|
688 |
|
689 |
+
# Fallback: if user asked for a chart and we didn't get SQL or chart yet,
|
690 |
# parse the most recent assistant text for lines like "LABEL: NUMBER" (bulleted or plain).
|
691 |
if chart_fig is None:
|
692 |
+
wants_chart, desired_type = detect_chart_preferences(question)
|
|
|
693 |
if wants_chart:
|
694 |
# Find the most recent assistant message with usable numeric pairs
|
695 |
candidate_text = ""
|
|
|
727 |
data.append({"label": label, "value": val})
|
728 |
logger.info(f"Fallback parse from text: extracted {len(data)} items for potential chart")
|
729 |
if len(data) >= 2:
|
|
|
730 |
chart_fig = generate_chart(
|
731 |
data=data,
|
732 |
chart_type=desired_type,
|
|
|
1034 |
# Append assistant message back into messages history
|
1035 |
chat_history.append({"role": "assistant", "content": assistant_message})
|
1036 |
|
1037 |
+
# If user asked for a chart but none was produced, try to build one
|
1038 |
+
# from the latest assistant text using the same fallback logic.
|
1039 |
+
if chart_fig is None:
|
1040 |
+
wants_chart, desired_type = detect_chart_preferences(question)
|
1041 |
+
if wants_chart and isinstance(assistant_message, str):
|
1042 |
+
candidate_text = assistant_message
|
1043 |
+
raw_lines = candidate_text.split('\n')
|
1044 |
+
norm_lines = []
|
1045 |
+
for l in raw_lines:
|
1046 |
+
s = l.strip().lstrip("•*\t -")
|
1047 |
+
if s:
|
1048 |
+
norm_lines.append(s)
|
1049 |
+
data = []
|
1050 |
+
for l in norm_lines:
|
1051 |
+
m = re.match(r"^(.+?):\s*([0-9][0-9.,]*)$", l)
|
1052 |
+
if m:
|
1053 |
+
label = re.sub(r"[*_`]+", "", m.group(1)).strip()
|
1054 |
+
try:
|
1055 |
+
val = float(m.group(2).replace(',', ''))
|
1056 |
+
except Exception:
|
1057 |
+
continue
|
1058 |
+
data.append({"label": label, "value": val})
|
1059 |
+
if len(data) >= 2:
|
1060 |
+
chart_fig = generate_chart(
|
1061 |
+
data=data,
|
1062 |
+
chart_type=desired_type,
|
1063 |
+
x="label",
|
1064 |
+
y="value",
|
1065 |
+
title="Distribución"
|
1066 |
+
)
|
1067 |
+
|
1068 |
logger.info("Response generation complete")
|
1069 |
return chat_history, chart_fig
|
1070 |
|