Commit
路
0369200
1
Parent(s):
eed8b79
INFO:__main__:Database connection successful INFO:__main__:Response generation complete
Browse files
app.py
CHANGED
|
@@ -12,6 +12,10 @@ import pandas as pd
|
|
| 12 |
import plotly.express as px
|
| 13 |
import plotly.graph_objects as go
|
| 14 |
from plotly.subplots import make_subplots
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
|
| 16 |
try:
|
| 17 |
# Intentar importar dependencias opcionales
|
|
@@ -319,21 +323,36 @@ else:
|
|
| 319 |
|
| 320 |
logger.info("="*50)
|
| 321 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 322 |
def extract_sql_query(text):
|
| 323 |
-
"""Extrae consultas SQL del texto
|
|
|
|
|
|
|
| 324 |
if not text:
|
| 325 |
return None
|
| 326 |
-
|
| 327 |
-
# Buscar
|
| 328 |
-
|
| 329 |
-
|
| 330 |
-
|
| 331 |
-
|
| 332 |
-
|
| 333 |
-
|
| 334 |
-
|
| 335 |
-
|
| 336 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 337 |
return None
|
| 338 |
|
| 339 |
def execute_sql_query(query, db_connection):
|
|
@@ -343,7 +362,11 @@ def execute_sql_query(query, db_connection):
|
|
| 343 |
|
| 344 |
try:
|
| 345 |
with db_connection._engine.connect() as connection:
|
| 346 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 347 |
rows = result.fetchall()
|
| 348 |
|
| 349 |
# Convertir los resultados a un formato legible
|
|
@@ -481,9 +504,9 @@ async def stream_agent_response(question: str, chat_history: List[List[str]]) ->
|
|
| 481 |
|
| 482 |
logger.info(f"Extracted response text: {response_text[:200]}...")
|
| 483 |
|
| 484 |
-
# Check if the response contains an SQL query
|
| 485 |
sql_query = extract_sql_query(response_text)
|
| 486 |
-
if sql_query:
|
| 487 |
logger.info(f"Detected SQL query: {sql_query}")
|
| 488 |
# Execute the query and update the response
|
| 489 |
db_connection, _ = setup_database_connection()
|
|
@@ -551,6 +574,8 @@ async def stream_agent_response(question: str, chat_history: List[List[str]]) ->
|
|
| 551 |
response_text += "\n\n鈿狅笍 No se pudo generar la visualizaci贸n de los datos."
|
| 552 |
else:
|
| 553 |
response_text += "\n\n鈿狅笍 No se pudo conectar a la base de datos para ejecutar la consulta."
|
|
|
|
|
|
|
| 554 |
|
| 555 |
# Fallback: if user asked for a chart (e.g., pie) and we didn't get SQL or chart yet,
|
| 556 |
# parse the most recent assistant text for lines like "LABEL: NUMBER" (bulleted or plain).
|
|
|
|
| 12 |
import plotly.express as px
|
| 13 |
import plotly.graph_objects as go
|
| 14 |
from plotly.subplots import make_subplots
|
| 15 |
+
try:
|
| 16 |
+
from sqlalchemy import text as sa_text
|
| 17 |
+
except Exception:
|
| 18 |
+
sa_text = None
|
| 19 |
|
| 20 |
try:
|
| 21 |
# Intentar importar dependencias opcionales
|
|
|
|
| 323 |
|
| 324 |
logger.info("="*50)
|
| 325 |
|
| 326 |
+
def looks_like_sql(s: str) -> bool:
|
| 327 |
+
"""Heuristic to check if a string looks like an executable SQL statement."""
|
| 328 |
+
if not s:
|
| 329 |
+
return False
|
| 330 |
+
s_strip = s.strip().lstrip("-- ")
|
| 331 |
+
# common starters
|
| 332 |
+
return bool(re.match(r"^(WITH|SELECT|INSERT|UPDATE|DELETE|CREATE|ALTER|DROP|TRUNCATE)\b", s_strip, re.IGNORECASE))
|
| 333 |
+
|
| 334 |
+
|
| 335 |
def extract_sql_query(text):
|
| 336 |
+
"""Extrae consultas SQL del texto. Acepta solo bloques etiquetados como ```sql
|
| 337 |
+
o cadenas que claramente parezcan SQL. Evita ejecutar texto gen茅rico.
|
| 338 |
+
"""
|
| 339 |
if not text:
|
| 340 |
return None
|
| 341 |
+
|
| 342 |
+
# Buscar TODOS los bloques en backticks y elegir los que sean 'sql'
|
| 343 |
+
for m in re.finditer(r"```(\w+)?\s*(.*?)```", text, re.DOTALL | re.IGNORECASE):
|
| 344 |
+
lang = (m.group(1) or '').lower()
|
| 345 |
+
body = (m.group(2) or '').strip()
|
| 346 |
+
if lang in {"sql", "postgresql", "mysql"} and looks_like_sql(body):
|
| 347 |
+
return body
|
| 348 |
+
|
| 349 |
+
# Si no hay bloques etiquetados, buscar una consulta SQL simple con palabras clave
|
| 350 |
+
simple = re.search(r"(WITH|SELECT|INSERT|UPDATE|DELETE|CREATE|ALTER|DROP|TRUNCATE)[\s\S]*?;", text, re.IGNORECASE)
|
| 351 |
+
if simple:
|
| 352 |
+
candidate = simple.group(0).strip()
|
| 353 |
+
if looks_like_sql(candidate):
|
| 354 |
+
return candidate
|
| 355 |
+
|
| 356 |
return None
|
| 357 |
|
| 358 |
def execute_sql_query(query, db_connection):
|
|
|
|
| 362 |
|
| 363 |
try:
|
| 364 |
with db_connection._engine.connect() as connection:
|
| 365 |
+
# Ensure SQLAlchemy receives a SQL expression
|
| 366 |
+
if sa_text is not None and isinstance(query, str):
|
| 367 |
+
result = connection.execute(sa_text(query))
|
| 368 |
+
else:
|
| 369 |
+
result = connection.execute(query)
|
| 370 |
rows = result.fetchall()
|
| 371 |
|
| 372 |
# Convertir los resultados a un formato legible
|
|
|
|
| 504 |
|
| 505 |
logger.info(f"Extracted response text: {response_text[:200]}...")
|
| 506 |
|
| 507 |
+
# Check if the response contains an SQL query and it truly looks like SQL
|
| 508 |
sql_query = extract_sql_query(response_text)
|
| 509 |
+
if sql_query and looks_like_sql(sql_query):
|
| 510 |
logger.info(f"Detected SQL query: {sql_query}")
|
| 511 |
# Execute the query and update the response
|
| 512 |
db_connection, _ = setup_database_connection()
|
|
|
|
| 574 |
response_text += "\n\n鈿狅笍 No se pudo generar la visualizaci贸n de los datos."
|
| 575 |
else:
|
| 576 |
response_text += "\n\n鈿狅笍 No se pudo conectar a la base de datos para ejecutar la consulta."
|
| 577 |
+
elif sql_query and not looks_like_sql(sql_query):
|
| 578 |
+
logger.info("Detected code block but it does not look like SQL; skipping execution.")
|
| 579 |
|
| 580 |
# Fallback: if user asked for a chart (e.g., pie) and we didn't get SQL or chart yet,
|
| 581 |
# parse the most recent assistant text for lines like "LABEL: NUMBER" (bulleted or plain).
|