import gradio as gr
import os
import json
from datetime import datetime
import logging
import threading
# Configuración de logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Importar módulos personalizados
from model_manager import ModelManager
from api_agent import APIAgent
from prompt_generator import PromptGenerator
# Inicializar componentes
model_manager = ModelManager()
api_agent = APIAgent()
prompt_generator = PromptGenerator()
class BATUTOChatbot:
def __init__(self):
self.conversation_history = []
self.config = {
'deepseek_api_key': '',
'openai_api_key': '',
'max_tokens': 400,
'temperature': 0.7
}
def update_config(self, deepseek_key, openai_key, max_tokens, temperature):
"""Actualiza la configuración desde la UI"""
updated = False
if deepseek_key:
self.config['deepseek_api_key'] = deepseek_key
updated = True
if openai_key:
self.config['openai_api_key'] = openai_key
updated = True
if max_tokens:
self.config['max_tokens'] = int(max_tokens)
updated = True
if temperature:
self.config['temperature'] = float(temperature)
updated = True
# Actualizar agentes
model_manager.set_config(self.config)
api_agent.set_config(self.config)
return 'Configuración actualizada' if updated else 'Sin cambios'
def get_system_status(self):
"""Obtiene el estado del sistema"""
has_deepseek = bool(self.config.get('deepseek_api_key'))
has_openai = bool(self.config.get('openai_api_key'))
models_loaded = model_manager.loaded
status_html = f'''
Estado del Sistema
Modelos locales: {'Cargados' if models_loaded else 'Cargando...'}
DeepSeek API: {'Configurada' if has_deepseek else 'No configurada'}
OpenAI API: {'Configurada' if has_openai else 'No configurada'}
Mensajes en sesión: {len(self.conversation_history)}
'''
return status_html
def chat_response(self, message, history):
"""Genera respuesta del chatbot optimizado para HF"""
if not message.strip():
return ''
# Mostrar indicador de typing
yield 'Procesando...'
try:
# Detectar intención y mejorar prompt
intent = prompt_generator.detect_intent(message)
enhanced_prompt = prompt_generator.enhance_prompt(message, intent)
# Intentar usar APIs primero
api_result = api_agent.generate_response(enhanced_prompt, intent['is_code'])
if api_result['response']:
# Usar respuesta de API
response_text = api_result['response']
source = api_result['source']
else:
# Usar modelo local como fallback
response_text = model_manager.generate_local_response(
enhanced_prompt,
intent['is_code'],
max_length=200
)
source = 'local'
# Agregar metadata a la respuesta
metadata = f'\n\n---\nFuente: {source.upper()}'
if intent['is_code']:
metadata += f' | Tipo: Código'
else:
metadata += f' | Tipo: Conversación'
full_response = response_text + metadata
# Guardar en historial
self.conversation_history.append({
'timestamp': datetime.now().isoformat(),
'user': message,
'bot': response_text,
'source': source,
'intent': intent
})
yield full_response
except Exception as e:
error_msg = f'Error: {str(e)}'
logger.error(f'Error en chat_response: {e}')
yield error_msg
def clear_conversation(self):
"""Limpia la conversación"""
self.conversation_history.clear()
return None, []
# Crear instancia del chatbot
chatbot = BATUTOChatbot()
# Cargar modelos al inicio (async)
def load_models_async():
logger.info('Cargando modelos en segundo plano...')
model_manager.load_models()
logger.info('Modelos cargados exitosamente')
# Iniciar carga de modelos
model_loader = threading.Thread(target=load_models_async, daemon=True)
model_loader.start()
# Configuración de la interfaz Gradio para HF
with gr.Blocks(
title='BATUTO Chatbot - Asistente Educativo',
theme=gr.themes.Soft(),
css='''
.gradio-container {
max-width: 1000px !important;
margin: auto;
}
.chat-container {
height: 500px;
}
.status-panel {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 20px;
border-radius: 10px;
color: white;
}
'''
) as demo:
gr.Markdown('''
# BATUTO Chatbot - Asistente Educativo
**Sistema inteligente con modelos locales y APIs externas**
*Desplegado en Hugging Face Spaces - Versión Optimizada*
''')
with gr.Row():
with gr.Column(scale=2):
# Área de chat
gr.Markdown('### Conversación')
chatbot_interface = gr.Chatbot(
label='Chat con BATUTO',
height=400,
show_copy_button=True,
container=True
)
msg = gr.Textbox(
label='Escribe tu mensaje',
placeholder='Pregunta sobre programación, explica conceptos, pide ejemplos...',
lines=2,
max_lines=4
)
with gr.Row():
submit_btn = gr.Button('Enviar', variant='primary')
clear_btn = gr.Button('Limpiar', variant='secondary')
with gr.Column(scale=1):
# Panel de estado
gr.Markdown('### Estado del Sistema')
status_display = gr.HTML()
# Configuración rápida
with gr.Accordion('Configuración Rápida', open=False):
with gr.Group():
deepseek_key = gr.Textbox(
label='DeepSeek API Key',
type='password',
placeholder='sk-...',
info='Opcional - para respuestas mejoradas'
)
openai_key = gr.Textbox(
label='OpenAI API Key',
type='password',
placeholder='sk-...',
info='Opcional - alternativa'
)
with gr.Row():
max_tokens = gr.Slider(
label='Tokens máx',
minimum=100,
maximum=800,
value=400,
step=50
)
temperature = gr.Slider(
label='Temperatura',
minimum=0.1,
maximum=1.0,
value=0.7,
step=0.1
)
save_config_btn = gr.Button('Guardar Config', size='sm')
config_output = gr.Textbox(label='Estado', interactive=False)
# Información
with gr.Accordion('Cómo usar', open=True):
gr.Markdown('''
**Ejemplos:**
- Muéstrame una función Python para ordenar listas
- Explica qué es machine learning
- Corrige este código: [tu código]
**Fuentes:**
1. DeepSeek API (si se configura)
2. OpenAI API (si se configura)
3. Modelos locales (fallback)
''')
# Event handlers
def handle_submit(message, history):
if not message.strip():
return '', history
return '', history + [[message, None]]
# Conectar el botón de enviar
submit_btn.click(
handle_submit,
inputs=[msg, chatbot_interface],
outputs=[msg, chatbot_interface]
).then(
chatbot.chat_response,
inputs=[msg, chatbot_interface],
outputs=[chatbot_interface]
)
# Enter también envía
msg.submit(
handle_submit,
inputs=[msg, chatbot_interface],
outputs=[msg, chatbot_interface]
).then(
chatbot.chat_response,
inputs=[msg, chatbot_interface],
outputs=[chatbot_interface]
)
# Limpiar chat
clear_btn.click(
chatbot.clear_conversation,
outputs=[msg, chatbot_interface]
)
# Configuración
save_config_btn.click(
chatbot.update_config,
inputs=[deepseek_key, openai_key, max_tokens, temperature],
outputs=[config_output]
).then(
chatbot.get_system_status,
outputs=[status_display]
)
# Actualizar estado al cargar
demo.load(
chatbot.get_system_status,
outputs=[status_display]
)
# Configuración específica para Hugging Face Spaces
if __name__ == '__main__':
demo.launch(
server_name='0.0.0.0',
server_port=7860,
share=True,
show_error=True,
debug=False
)