Jeremy Live
commited on
Commit
路
84dd234
1
Parent(s):
483fa34
add missing function
Browse files
app.py
CHANGED
@@ -22,6 +22,164 @@ logger = logging.getLogger(__name__)
|
|
22 |
# Flask app initialization
|
23 |
flask_app = Flask(__name__)
|
24 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
25 |
# Almacenamiento en memoria de los mensajes
|
26 |
message_store: Dict[str, str] = {}
|
27 |
|
@@ -68,11 +226,11 @@ def handle_ask():
|
|
68 |
if llm_error:
|
69 |
return jsonify({'error': f'Error al inicializar LLM: {llm_error}'}), 500
|
70 |
|
71 |
-
|
72 |
if db_error:
|
73 |
return jsonify({'error': f'Error de conexi贸n a la base de datos: {db_error}'}), 500
|
74 |
|
75 |
-
agent, agent_error = create_agent(llm,
|
76 |
if agent_error:
|
77 |
return jsonify({'error': f'Error al crear el agente: {agent_error}'}), 500
|
78 |
|
@@ -130,7 +288,7 @@ def create_application():
|
|
130 |
|
131 |
return "", chat_history
|
132 |
|
133 |
-
|
134 |
"""Generate bot response for messages-format chat history and return optional chart figure."""
|
135 |
if not chat_history:
|
136 |
return chat_history, None
|
@@ -162,7 +320,7 @@ def create_application():
|
|
162 |
i += 1
|
163 |
|
164 |
# Call the agent for this new user question
|
165 |
-
assistant_message, chart_fig =
|
166 |
|
167 |
# Append assistant message back into messages history
|
168 |
chat_history.append({"role": "assistant", "content": assistant_message})
|
@@ -184,7 +342,7 @@ def create_application():
|
|
184 |
fn=user_message,
|
185 |
inputs=[question_input, chatbot],
|
186 |
outputs=[question_input, chatbot],
|
187 |
-
queue=
|
188 |
).then(
|
189 |
fn=bot_response,
|
190 |
inputs=[chatbot],
|
@@ -197,7 +355,7 @@ def create_application():
|
|
197 |
fn=user_message,
|
198 |
inputs=[question_input, chatbot],
|
199 |
outputs=[question_input, chatbot],
|
200 |
-
queue=
|
201 |
).then(
|
202 |
fn=bot_response,
|
203 |
inputs=[chatbot],
|
|
|
22 |
# Flask app initialization
|
23 |
flask_app = Flask(__name__)
|
24 |
|
25 |
+
# Importaciones adicionales necesarias
|
26 |
+
import os
|
27 |
+
from dotenv import load_dotenv
|
28 |
+
from sqlalchemy import create_engine, text
|
29 |
+
from sqlalchemy.exc import SQLAlchemyError
|
30 |
+
from langchain_google_genai import ChatGoogleGenerativeAI
|
31 |
+
from langchain_community.agent_toolkits import create_sql_agent
|
32 |
+
from langchain_community.utilities import SQLDatabase
|
33 |
+
from langgraph.prebuilt import create_react_agent
|
34 |
+
|
35 |
+
# Cargar variables de entorno
|
36 |
+
load_dotenv()
|
37 |
+
|
38 |
+
def initialize_llm():
|
39 |
+
"""Inicializar el modelo LLM de Google Gemini."""
|
40 |
+
try:
|
41 |
+
api_key = os.getenv('GOOGLE_API_KEY')
|
42 |
+
if not api_key:
|
43 |
+
return None, "No se encontr贸 GOOGLE_API_KEY en las variables de entorno"
|
44 |
+
|
45 |
+
llm = ChatGoogleGenerativeAI(
|
46 |
+
model="gemini-2.0-flash",
|
47 |
+
google_api_key=api_key,
|
48 |
+
temperature=0.1,
|
49 |
+
convert_system_message_to_human=True
|
50 |
+
)
|
51 |
+
return llm, None
|
52 |
+
except Exception as e:
|
53 |
+
return None, str(e)
|
54 |
+
|
55 |
+
def setup_database_connection():
|
56 |
+
"""Configurar la conexi贸n a la base de datos."""
|
57 |
+
try:
|
58 |
+
# Obtener configuraci贸n de la base de datos
|
59 |
+
db_user = os.getenv('DB_USER')
|
60 |
+
db_password = os.getenv('DB_PASSWORD')
|
61 |
+
db_host = os.getenv('DB_HOST', 'localhost')
|
62 |
+
db_name = os.getenv('DB_NAME')
|
63 |
+
|
64 |
+
if not all([db_user, db_password, db_name]):
|
65 |
+
return None, "Faltan variables de entorno para la conexi贸n a la base de datos"
|
66 |
+
|
67 |
+
# Crear string de conexi贸n
|
68 |
+
connection_string = f"mysql+pymysql://{db_user}:{db_password}@{db_host}/{db_name}"
|
69 |
+
|
70 |
+
# Crear engine de SQLAlchemy y probar la conexi贸n
|
71 |
+
engine = create_engine(connection_string)
|
72 |
+
with engine.connect() as conn:
|
73 |
+
conn.execute(text("SELECT 1"))
|
74 |
+
|
75 |
+
return connection_string, None
|
76 |
+
except Exception as e:
|
77 |
+
return None, str(e)
|
78 |
+
|
79 |
+
def create_agent(llm, connection_string):
|
80 |
+
"""Crear el agente SQL."""
|
81 |
+
try:
|
82 |
+
if not llm or not connection_string:
|
83 |
+
return None, "LLM o conexi贸n a base de datos no proporcionados"
|
84 |
+
|
85 |
+
# Crear la base de datos SQL
|
86 |
+
db = SQLDatabase.from_uri(connection_string)
|
87 |
+
|
88 |
+
# Crear el agente SQL
|
89 |
+
agent = create_sql_agent(
|
90 |
+
llm=llm,
|
91 |
+
db=db,
|
92 |
+
agent_type="zero-shot-react-description",
|
93 |
+
verbose=True,
|
94 |
+
return_intermediate_steps=True
|
95 |
+
)
|
96 |
+
|
97 |
+
return agent, None
|
98 |
+
except Exception as e:
|
99 |
+
return None, str(e)
|
100 |
+
|
101 |
+
def stream_agent_response(question: str, chat_history: List[List[str]] = None) -> Tuple[str, Optional[go.Figure]]:
|
102 |
+
"""Procesar la respuesta del agente y generar visualizaciones si es necesario."""
|
103 |
+
try:
|
104 |
+
# Inicializar componentes
|
105 |
+
llm, llm_error = initialize_llm()
|
106 |
+
if llm_error:
|
107 |
+
return f"Error al inicializar LLM: {llm_error}", None
|
108 |
+
|
109 |
+
connection_string, db_error = setup_database_connection()
|
110 |
+
if db_error:
|
111 |
+
return f"Error de conexi贸n a base de datos: {db_error}", None
|
112 |
+
|
113 |
+
agent, agent_error = create_agent(llm, connection_string)
|
114 |
+
if agent_error:
|
115 |
+
return f"Error al crear el agente: {agent_error}", None
|
116 |
+
|
117 |
+
# Ejecutar la consulta
|
118 |
+
response = agent.invoke({"input": question})
|
119 |
+
|
120 |
+
# Extraer la respuesta
|
121 |
+
if hasattr(response, 'output'):
|
122 |
+
response_text = response.output
|
123 |
+
elif isinstance(response, dict) and 'output' in response:
|
124 |
+
response_text = response['output']
|
125 |
+
else:
|
126 |
+
response_text = str(response)
|
127 |
+
|
128 |
+
# Verificar si hay resultados de SQL que podr铆an visualizarse
|
129 |
+
chart_fig = None
|
130 |
+
if hasattr(response, 'intermediate_steps'):
|
131 |
+
for step in response.intermediate_steps:
|
132 |
+
if len(step) > 1 and 'sql_query' in str(step[0]).lower():
|
133 |
+
# Intentar ejecutar la consulta y crear visualizaci贸n
|
134 |
+
try:
|
135 |
+
query = str(step[0]).split('sql_query:')[1].split('\n')[0].strip()
|
136 |
+
if 'SELECT' in query.upper():
|
137 |
+
df = pd.read_sql_query(query, create_engine(connection_string))
|
138 |
+
if len(df.columns) >= 2:
|
139 |
+
fig = px.bar(df, x=df.columns[0], y=df.columns[1])
|
140 |
+
chart_fig = fig
|
141 |
+
except:
|
142 |
+
pass
|
143 |
+
|
144 |
+
return response_text, chart_fig
|
145 |
+
|
146 |
+
except Exception as e:
|
147 |
+
return f"Error al procesar la solicitud: {str(e)}", None
|
148 |
+
|
149 |
+
def create_ui():
|
150 |
+
"""Crear la interfaz de usuario de Gradio."""
|
151 |
+
with gr.Blocks(title="馃 Asistente SQL con Gemini", theme=gr.themes.Soft()) as demo:
|
152 |
+
gr.Markdown("# 馃 Asistente de Base de Datos SQL")
|
153 |
+
gr.Markdown("Pregunta cualquier cosa sobre tu base de datos en lenguaje natural")
|
154 |
+
|
155 |
+
with gr.Row():
|
156 |
+
with gr.Column(scale=2):
|
157 |
+
chatbot = gr.Chatbot(
|
158 |
+
label="Chat",
|
159 |
+
type="messages",
|
160 |
+
height=400
|
161 |
+
)
|
162 |
+
|
163 |
+
with gr.Row():
|
164 |
+
question_input = gr.Textbox(
|
165 |
+
label="Tu pregunta",
|
166 |
+
placeholder="Ej: 驴Cu谩ntos usuarios hay registrados?",
|
167 |
+
lines=2,
|
168 |
+
scale=4
|
169 |
+
)
|
170 |
+
submit_button = gr.Button("馃摛 Enviar", scale=1)
|
171 |
+
|
172 |
+
with gr.Column(scale=1):
|
173 |
+
chart_display = gr.Plot(
|
174 |
+
label="Visualizaci贸n de datos",
|
175 |
+
height=400
|
176 |
+
)
|
177 |
+
|
178 |
+
# Campo oculto para el estado de salida
|
179 |
+
streaming_output_display = gr.HTML(visible=False)
|
180 |
+
|
181 |
+
return demo, chatbot, chart_display, question_input, submit_button, streaming_output_display
|
182 |
+
|
183 |
# Almacenamiento en memoria de los mensajes
|
184 |
message_store: Dict[str, str] = {}
|
185 |
|
|
|
226 |
if llm_error:
|
227 |
return jsonify({'error': f'Error al inicializar LLM: {llm_error}'}), 500
|
228 |
|
229 |
+
connection_string, db_error = setup_database_connection()
|
230 |
if db_error:
|
231 |
return jsonify({'error': f'Error de conexi贸n a la base de datos: {db_error}'}), 500
|
232 |
|
233 |
+
agent, agent_error = create_agent(llm, connection_string)
|
234 |
if agent_error:
|
235 |
return jsonify({'error': f'Error al crear el agente: {agent_error}'}), 500
|
236 |
|
|
|
288 |
|
289 |
return "", chat_history
|
290 |
|
291 |
+
def bot_response(chat_history: List[Dict[str, str]]) -> Tuple[List[Dict[str, str]], Optional[go.Figure]]:
|
292 |
"""Generate bot response for messages-format chat history and return optional chart figure."""
|
293 |
if not chat_history:
|
294 |
return chat_history, None
|
|
|
320 |
i += 1
|
321 |
|
322 |
# Call the agent for this new user question
|
323 |
+
assistant_message, chart_fig = stream_agent_response(question, pair_history)
|
324 |
|
325 |
# Append assistant message back into messages history
|
326 |
chat_history.append({"role": "assistant", "content": assistant_message})
|
|
|
342 |
fn=user_message,
|
343 |
inputs=[question_input, chatbot],
|
344 |
outputs=[question_input, chatbot],
|
345 |
+
queue=False
|
346 |
).then(
|
347 |
fn=bot_response,
|
348 |
inputs=[chatbot],
|
|
|
355 |
fn=user_message,
|
356 |
inputs=[question_input, chatbot],
|
357 |
outputs=[question_input, chatbot],
|
358 |
+
queue=False
|
359 |
).then(
|
360 |
fn=bot_response,
|
361 |
inputs=[chatbot],
|