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()