Jeremy Live
commited on
Commit
·
9f4148c
1
Parent(s):
70c3587
mhv2
Browse files
app.py
CHANGED
@@ -482,6 +482,30 @@ def is_db_intent(question: str) -> bool:
|
|
482 |
except Exception:
|
483 |
return False
|
484 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
485 |
def generate_plot(data, x_col, y_col, title, x_label, y_label):
|
486 |
"""Generate a plot from data and return the file path."""
|
487 |
plt.figure(figsize=(10, 6))
|
@@ -746,44 +770,21 @@ async def stream_agent_response(question: str, chat_history: List[List[str]], se
|
|
746 |
logger.error(f"Second-pass SQL synthesis failed: {e}")
|
747 |
|
748 |
# Fallback: if user asked for a chart and we didn't get SQL or chart yet,
|
749 |
-
#
|
750 |
if chart_fig is None:
|
751 |
wants_chart, desired_type = detect_chart_preferences(question)
|
752 |
if wants_chart:
|
753 |
-
# Find the most recent assistant message with usable numeric pairs
|
754 |
candidate_text = ""
|
755 |
-
if
|
|
|
|
|
|
|
|
|
756 |
for pair in reversed(chat_history):
|
757 |
if len(pair) >= 2 and isinstance(pair[1], str) and pair[1].strip():
|
758 |
candidate_text = pair[1]
|
759 |
break
|
760 |
-
|
761 |
-
if not candidate_text and isinstance(response_text, str) and response_text.strip():
|
762 |
-
candidate_text = response_text
|
763 |
-
if candidate_text:
|
764 |
-
raw_lines = candidate_text.split('\n')
|
765 |
-
# Normalize lines: strip bullets and markdown symbols
|
766 |
-
norm_lines = []
|
767 |
-
for l in raw_lines:
|
768 |
-
s = l.strip()
|
769 |
-
if not s:
|
770 |
-
continue
|
771 |
-
s = s.lstrip("•*-\t ")
|
772 |
-
# Remove surrounding markdown emphasis from labels later
|
773 |
-
norm_lines.append(s)
|
774 |
-
data = []
|
775 |
-
for l in norm_lines:
|
776 |
-
# Accept patterns like "**LABEL**: 123" or "LABEL: 1,234"
|
777 |
-
m = re.match(r"^(.+?):\s*([0-9][0-9.,]*)$", l)
|
778 |
-
if m:
|
779 |
-
label = m.group(1).strip()
|
780 |
-
# Strip common markdown emphasis
|
781 |
-
label = re.sub(r"[*_`]+", "", label).strip()
|
782 |
-
try:
|
783 |
-
val = float(m.group(2).replace(',', ''))
|
784 |
-
except Exception:
|
785 |
-
continue
|
786 |
-
data.append({"label": label, "value": val})
|
787 |
logger.info(f"Fallback parse from text: extracted {len(data)} items for potential chart")
|
788 |
if len(data) >= 2:
|
789 |
chart_fig = generate_chart(
|
@@ -793,8 +794,14 @@ async def stream_agent_response(question: str, chat_history: List[List[str]], se
|
|
793 |
y="value",
|
794 |
title="Distribución"
|
795 |
)
|
796 |
-
|
797 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
798 |
|
799 |
# Update the assistant's message with the response
|
800 |
assistant_message["content"] = response_text
|
|
|
482 |
except Exception:
|
483 |
return False
|
484 |
|
485 |
+
def extract_label_value_pairs(text: str) -> List[Dict[str, Union[str, float]]]:
|
486 |
+
"""Extract pairs like 'LABEL: NUMBER' from free text."""
|
487 |
+
pairs: List[Dict[str, Union[str, float]]] = []
|
488 |
+
if not text:
|
489 |
+
return pairs
|
490 |
+
try:
|
491 |
+
raw_lines = text.split('\n')
|
492 |
+
for line in raw_lines:
|
493 |
+
s = line.strip()
|
494 |
+
if not s:
|
495 |
+
continue
|
496 |
+
s = s.lstrip('•*\t -')
|
497 |
+
m = re.match(r"^(.+?):\s*([0-9][0-9.,]*)$", s)
|
498 |
+
if m:
|
499 |
+
label = re.sub(r"[*_`]+", "", m.group(1)).strip()
|
500 |
+
try:
|
501 |
+
value = float(m.group(2).replace(',', ''))
|
502 |
+
pairs.append({"label": label, "value": value})
|
503 |
+
except Exception:
|
504 |
+
continue
|
505 |
+
return pairs
|
506 |
+
except Exception:
|
507 |
+
return pairs
|
508 |
+
|
509 |
def generate_plot(data, x_col, y_col, title, x_label, y_label):
|
510 |
"""Generate a plot from data and return the file path."""
|
511 |
plt.figure(figsize=(10, 6))
|
|
|
770 |
logger.error(f"Second-pass SQL synthesis failed: {e}")
|
771 |
|
772 |
# Fallback: if user asked for a chart and we didn't get SQL or chart yet,
|
773 |
+
# use the MOST RECENT bot text (this turn or previous) and parse label:value pairs.
|
774 |
if chart_fig is None:
|
775 |
wants_chart, desired_type = detect_chart_preferences(question)
|
776 |
if wants_chart:
|
|
|
777 |
candidate_text = ""
|
778 |
+
# Prefer THIS TURN's response_text if it's non-empty
|
779 |
+
if isinstance(response_text, str) and response_text.strip():
|
780 |
+
candidate_text = response_text
|
781 |
+
# Otherwise look back at the latest assistant turn in history
|
782 |
+
if not candidate_text and chat_history:
|
783 |
for pair in reversed(chat_history):
|
784 |
if len(pair) >= 2 and isinstance(pair[1], str) and pair[1].strip():
|
785 |
candidate_text = pair[1]
|
786 |
break
|
787 |
+
data = extract_label_value_pairs(candidate_text)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
788 |
logger.info(f"Fallback parse from text: extracted {len(data)} items for potential chart")
|
789 |
if len(data) >= 2:
|
790 |
chart_fig = generate_chart(
|
|
|
794 |
y="value",
|
795 |
title="Distribución"
|
796 |
)
|
797 |
+
if chart_fig is not None and not response_text.strip():
|
798 |
+
# Provide a helpful text if the agent didn't produce one
|
799 |
+
response_text = (
|
800 |
+
f"Gráfico {('de barras' if desired_type=='bar' else desired_type)} "
|
801 |
+
f"con {len(data)} categorías basado en los datos previos."
|
802 |
+
)
|
803 |
+
if chart_fig is not None:
|
804 |
+
logger.info(f"Chart generated from text fallback: type={desired_type}, items={len(data)}")
|
805 |
|
806 |
# Update the assistant's message with the response
|
807 |
assistant_message["content"] = response_text
|