File size: 8,352 Bytes
f639c56
dc2d325
24f29f0
f639c56
 
1fe11cb
 
 
 
f639c56
1fe11cb
 
 
 
483fa34
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7b28785
483fa34
 
 
7b28785
483fa34
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f639c56
0f16c64
f639c56
713a6e6
 
 
683b6ad
f639c56
0f16c64
 
5758499
e3d4c98
483fa34
 
5758499
0f16c64
d89427c
 
713a6e6
 
d89427c
713a6e6
d89427c
332a246
 
d89427c
 
 
 
332a246
713a6e6
7b28785
683b6ad
55ff70d
683b6ad
d89427c
 
 
 
683b6ad
d89427c
332a246
d89427c
332a246
d89427c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fa13896
7b28785
d89427c
 
 
 
332a246
683b6ad
d89427c
332a246
6aebc39
332a246
d89427c
 
683b6ad
713a6e6
 
 
332a246
 
713a6e6
 
 
7b28785
713a6e6
 
683b6ad
 
713a6e6
 
 
332a246
 
713a6e6
 
 
7b28785
713a6e6
 
683b6ad
 
713a6e6
 
 
f639c56
713a6e6
 
f639c56
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
483fa34
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
import os
import sys
import re
import gradio as gr
import json
import tempfile
import base64
import io
from typing import List, Dict, Any, Optional, Tuple, Union
import logging
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
from flask import Flask, request, jsonify
import uuid

# Configuración de logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# Flask app initialization
flask_app = Flask(__name__)

# Almacenamiento en memoria de los mensajes
message_store: Dict[str, str] = {}

@flask_app.route('/user_message', methods=['POST'])
def handle_user_message():
    try:
        data = request.get_json()
        if not data or 'message' not in data:
            return jsonify({'error': 'Se requiere el campo message'}), 400
            
        user_message = data['message']
        
        # Generar un ID único para este mensaje
        message_id = str(uuid.uuid4())
        
        # Almacenar el mensaje
        message_store[message_id] = user_message
        
        return jsonify({
            'message_id': message_id,
            'status': 'success'
        })
        
    except Exception as e:
        return jsonify({'error': str(e)}), 500

@flask_app.route('/ask', methods=['POST'])
def handle_ask():
    try:
        data = request.get_json()
        if not data or 'message_id' not in data:
            return jsonify({'error': 'Se requiere el campo message_id'}), 400
            
        message_id = data['message_id']
        
        # Recuperar el mensaje almacenado
        if message_id not in message_store:
            return jsonify({'error': 'ID de mensaje no encontrado'}), 404
            
        user_message = message_store[message_id]
        
        # Inicializar componentes necesarios
        llm, llm_error = initialize_llm()
        if llm_error:
            return jsonify({'error': f'Error al inicializar LLM: {llm_error}'}), 500
            
        db_connection, db_error = setup_database_connection()
        if db_error:
            return jsonify({'error': f'Error de conexión a la base de datos: {db_error}'}), 500
            
        agent, agent_error = create_agent(llm, db_connection)
        if agent_error:
            return jsonify({'error': f'Error al crear el agente: {agent_error}'}), 500
        
        # Obtener respuesta del agente
        response = agent.invoke({"input": user_message})
        
        # Procesar la respuesta
        if hasattr(response, 'output') and response.output:
            response_text = response.output
        elif isinstance(response, str):
            response_text = response
        elif hasattr(response, 'get') and callable(response.get) and 'output' in response:
            response_text = response['output']
        else:
            response_text = str(response)
        
        # Eliminar el mensaje almacenado después de procesarlo
        del message_store[message_id]
        
        return jsonify({
            'response': response_text,
            'status': 'success'
        })
        
    except Exception as e:
        return jsonify({'error': str(e)}), 500

# ... (resto del código existente sin cambios) ...

def create_application():
    """Create and configure the Gradio application."""
    # Create the UI components
    demo, chatbot, chart_display, question_input, submit_button, streaming_output_display = create_ui()
    
    # Montar la API Flask en la aplicación Gradio
    if os.getenv('SPACE_ID'):
        demo = gr.mount_gradio_app(
            flask_app,
            demo,
            "/api"  # Prefijo para los endpoints de la API
        )
    
    def user_message(user_input: str, chat_history: List[Dict[str, str]]) -> Tuple[str, List[Dict[str, str]]]:
        """Add user message to chat history (messages format) and clear input."""
        if not user_input.strip():
            return "", chat_history

        logger.info(f"User message: {user_input}")

        if chat_history is None:
            chat_history = []

        # Append user message in messages format
        chat_history.append({"role": "user", "content": user_input})

        return "", chat_history
    
    async def bot_response(chat_history: List[Dict[str, str]]) -> Tuple[List[Dict[str, str]], Optional[go.Figure]]:
        """Generate bot response for messages-format chat history and return optional chart figure."""
        if not chat_history:
            return chat_history, None

        # Ensure last message is a user turn awaiting assistant reply
        last = chat_history[-1]
        if not isinstance(last, dict) or last.get("role") != "user" or not last.get("content"):
            return chat_history, None

        try:
            question = last["content"]
            logger.info(f"Processing question: {question}")

            # Convert prior messages to pair history for stream_agent_response()
            pair_history: List[List[str]] = []
            i = 0
            while i < len(chat_history) - 1:
                m1 = chat_history[i]
                m2 = chat_history[i + 1] if i + 1 < len(chat_history) else None
                if (
                    isinstance(m1, dict)
                    and m1.get("role") == "user"
                    and isinstance(m2, dict)
                    and m2.get("role") == "assistant"
                ):
                    pair_history.append([m1.get("content", ""), m2.get("content", "")])
                    i += 2
                else:
                    i += 1

            # Call the agent for this new user question
            assistant_message, chart_fig = await stream_agent_response(question, pair_history)

            # Append assistant message back into messages history
            chat_history.append({"role": "assistant", "content": assistant_message})

            logger.info("Response generation complete")
            return chat_history, chart_fig

        except Exception as e:
            error_msg = f"## ❌ Error\n\nError al procesar la solicitud:\n\n```\n{str(e)}\n```"
            logger.error(error_msg, exc_info=True)
            # Ensure we add an assistant error message for the UI
            chat_history.append({"role": "assistant", "content": error_msg})
            return chat_history, None
    
    # Event handlers
    with demo:
        # Handle form submission
        msg_submit = question_input.submit(
            fn=user_message,
            inputs=[question_input, chatbot],
            outputs=[question_input, chatbot],
            queue=True
        ).then(
            fn=bot_response,
            inputs=[chatbot],
            outputs=[chatbot, chart_display],
            api_name="ask"
        )
        
        # Handle button click
        btn_click = submit_button.click(
            fn=user_message,
            inputs=[question_input, chatbot],
            outputs=[question_input, chatbot],
            queue=True
        ).then(
            fn=bot_response,
            inputs=[chatbot],
            outputs=[chatbot, chart_display]
        )
    
    return demo

# Create the application
demo = create_application()

# Configuración para Hugging Face Spaces
def get_app():
    """Obtiene la instancia de la aplicación Gradio para Hugging Face Spaces."""
    # Verificar si estamos en un entorno de Hugging Face Spaces
    if os.getenv('SPACE_ID'):
        # Configuración específica para Spaces
        demo.title = "🤖 Asistente de Base de Datos SQL (Demo)"
        demo.description = """
        Este es un demo del asistente de base de datos SQL. 
        Para usar la versión completa con conexión a base de datos, clona este espacio y configura las variables de entorno.
        """
    
    return demo

# Para desarrollo local
if __name__ == "__main__":
    # Verificar si se debe ejecutar Flask o Gradio
    if os.environ.get('RUN_FLASK', 'false').lower() == 'true':
        # Ejecutar solo el servidor Flask
        port = int(os.environ.get('PORT', 5000))
        flask_app.run(host='0.0.0.0', port=port)
    else:
        # Configuración para desarrollo local - versión simplificada para Gradio 5.x
        demo.launch(
            server_name="0.0.0.0",
            server_port=7860,
            debug=True,
            share=False
        )