Spaces:
Sleeping
Sleeping
from os import getenv | |
from dotenv import load_dotenv | |
import gradio as gr | |
import os | |
import json | |
from langchain_huggingface import HuggingFaceEmbeddings | |
from langchain_chroma import Chroma | |
from langchain_openai import ChatOpenAI | |
from langchain.schema import Document | |
# Cargar variables de entorno desde un archivo .env | |
load_dotenv() | |
# Inicializar el modelo de lenguaje de OpenAI | |
llm = ChatOpenAI( | |
openai_api_key=getenv("OPENROUTER_API_KEY"), | |
openai_api_base=getenv("OPENROUTER_BASE_URL"), | |
model_name="openai/gpt-4o", | |
model_kwargs={ | |
"extra_headers": { | |
"Helicone-Auth": f"Bearer " + getenv("HELICONE_API_KEY") | |
} | |
}, | |
) | |
# Cargar datos de preguntas frecuentes desde un archivo JSON | |
faqs_path = r"faqs.json" | |
with open(faqs_path, "r") as file: | |
faqs_data = json.load(file) | |
# Cargar datos del menú desde un archivo JSON | |
menu_path = r"menu.json" | |
with open(menu_path, "r") as file: | |
menu_data = json.load(file) | |
# Definir la ruta del archivo de reservas | |
reservas_path = r"reservas.json" | |
# Definir la ruta del archivo PDF del menú | |
menu_pdf_path = r"menu.pdf" | |
# Crear el archivo de reservas si no existe | |
if not os.path.exists(reservas_path): | |
with open(reservas_path, "w") as file: | |
json.dump([], file) | |
# Cargar datos de reservas desde un archivo JSON | |
with open(reservas_path, "r") as file: | |
reservas_data = json.load(file) | |
# Crear documentos a partir de las preguntas frecuentes | |
faqs_documents = [ | |
Document( | |
page_content=pair["question"] + " " + pair["answer"], | |
metadata={"id": str(i)} | |
) | |
for i, pair in enumerate(faqs_data) | |
] | |
# Crear documentos a partir del menú | |
menu_documents = [ | |
Document( | |
page_content=f"{nombre}: Categoría: {info.get('categoria', 'Desconocida')}, " | |
f"Alérgenos: {', '.join(info.get('alergenos', [])) if info.get('alergenos') else 'Ninguno'}, " | |
f"Precio: ${info.get('precio', 'Desconocido')}, " | |
f"Descripción: {info.get('descripcion', 'Sin descripción')}, " | |
f"Sin Gluten: {'Sí' if info.get('sin_gluten') else 'No'}, " | |
f"Vegan: {'Sí' if info.get('vegan') else 'No'}", | |
metadata={"tipo": "menu"} | |
) | |
for nombre, info in menu_data.items() | |
] | |
# Inicializar el modelo de embeddings de HuggingFace | |
embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-mpnet-base-v2") | |
# Inicializar la tienda de vectores Chroma | |
vectorstore = Chroma(embedding_function=embeddings) | |
# Agregar documentos de preguntas frecuentes y menú a la tienda de vectores | |
vectorstore.add_documents(faqs_documents) | |
vectorstore.add_documents(menu_documents) | |
# Estado de la reserva | |
estado_reserva = {} | |
# Función del chatbot | |
def chatbot(message, history): | |
global estado_reserva | |
message_lower = message.lower() | |
# Palabras clave para mostrar el menú | |
menu_keywords = [ | |
"menu completo", "menú completo", "carta completa", | |
"ver menú", "ver menu", "mostrar menú", "mostrar menu", "quiero ver el menú", | |
"quiero ver la carta", "mostrar la carta", "ver la carta", "enséñame el menú", | |
"enséñame la carta", "dame el menú", "dame la carta", "muéstrame el menú", | |
"muéstrame la carta", "quiero el menú", "quiero la carta", "menu por favor", | |
"menú por favor", "carta por favor", "puedo ver el menú", "puedo ver la carta" | |
] | |
# Mostrar imagen del menú si el mensaje coincide con alguna palabra clave | |
if any(message_lower.strip() == word for word in menu_keywords): | |
yield gr.Image("menu.png") | |
return | |
# Continuar con el proceso de reserva si está en proceso | |
if estado_reserva.get("en_proceso"): | |
if "dia" not in estado_reserva: | |
estado_reserva["dia"] = message | |
yield "¿A qué hora deseas la reserva? (Formato HH:MM)" | |
return | |
elif "hora" not in estado_reserva: | |
estado_reserva["hora"] = message | |
yield "¿Para cuántas personas será la reserva?" | |
return | |
elif "personas" not in estado_reserva: | |
estado_reserva["personas"] = message | |
yield "A nombre de quién será la reserva?" | |
return | |
elif "nombre" not in estado_reserva: | |
estado_reserva["nombre"] = message | |
yield "(Opcional) Proporcione un número de teléfono de contacto o escriba 'no' para omitirlo." | |
return | |
elif "telefono" not in estado_reserva: | |
estado_reserva["telefono"] = message if message.lower() != "no" else "No proporcionado" | |
# Guardar la nueva reserva en el archivo JSON | |
nueva_reserva = { | |
"nombre": estado_reserva["nombre"], | |
"dia": estado_reserva["dia"], | |
"hora": estado_reserva["hora"], | |
"personas": estado_reserva["personas"], | |
"telefono": estado_reserva["telefono"] | |
} | |
with open(reservas_path, "r") as file: | |
reservas_actuales = json.load(file) | |
reservas_actuales.append(nueva_reserva) | |
with open(reservas_path, "w") as file: | |
json.dump(reservas_actuales, file, indent=2) | |
estado_reserva = {} | |
yield f"✅ ¡Reserva guardada con éxito! Aquí están los detalles:\n{json.dumps(nueva_reserva, indent=2)}" | |
return | |
# Iniciar el proceso de reserva si se menciona en el mensaje | |
if "reserva" in message_lower or "quiero reservar" in message_lower: | |
estado_reserva["en_proceso"] = True | |
yield "¿Para qué día quieres hacer la reserva? (Formato DD/MM/AAAA)" | |
return | |
# Buscar documentos relevantes en la tienda de vectores | |
relevant_docs = vectorstore.similarity_search(message) | |
# Crear el contexto para el modelo de lenguaje | |
context_text = "\n\n".join([doc.page_content for doc in relevant_docs]) | |
final_prompt = ( | |
"Eres un asistente virtual en un restaurante. Puedes responder preguntas sobre el menú, las reservas y las preguntas frecuentes. " | |
"Si el usuario menciona restricciones dietéticas, haz recomendaciones basadas en el menú disponible.\n\n" | |
f"{json.dumps(menu_data, indent=2)}\n\n" | |
f"Contexto relevante encontrado en la base de datos:\n{context_text}\n\n" | |
f"Pregunta: {message}\n" | |
"Respuesta:" | |
) | |
# Generar la respuesta del modelo de lenguaje | |
messages = [{"role": "user", "content": final_prompt}] | |
response = llm.stream(messages) | |
partial_response = "" | |
for chunk in response: | |
if chunk and hasattr(chunk, "content"): | |
content = chunk.content | |
if content is not None: | |
partial_response += content | |
yield partial_response | |
# Configurar la interfaz de Gradio | |
demo = gr.ChatInterface( | |
chatbot, | |
chatbot=gr.Chatbot(height=400, type="messages"), | |
textbox=gr.Textbox(placeholder="Escribe tu mensaje aquí...", container=False, scale=7), | |
title="ChatBot Restaurante", | |
description="Asistente virtual para reservas, menú y preguntas frecuentes.", | |
theme="ocean", | |
examples=[ | |
"¿Cuáles son los horarios del restaurante?", | |
"¿Dónde están ubicados?", | |
"¿Aceptan pagos con tarjeta?", | |
"Quiero ver la carta", | |
"Quiero hacer una reserva" | |
], | |
type="messages", | |
editable=True, | |
save_history=True, | |
) | |
# Ejecutar la aplicación | |
if __name__ == "__main__": | |
demo.queue().launch() | |