File size: 7,548 Bytes
193fffb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
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()