manumc commited on
Commit
193fffb
·
verified ·
1 Parent(s): 3995dc5

Upload 5 files

Browse files
Files changed (6) hide show
  1. .gitattributes +1 -0
  2. app.py +212 -0
  3. faqs.json +78 -0
  4. menu.json +147 -0
  5. menu.png +3 -0
  6. reservas.json +44 -0
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ menu.png filter=lfs diff=lfs merge=lfs -text
app.py ADDED
@@ -0,0 +1,212 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from os import getenv
2
+ from dotenv import load_dotenv
3
+ import gradio as gr
4
+ import os
5
+ import json
6
+ from langchain_huggingface import HuggingFaceEmbeddings
7
+ from langchain_chroma import Chroma
8
+ from langchain_openai import ChatOpenAI
9
+ from langchain.schema import Document
10
+
11
+ # Cargar variables de entorno desde un archivo .env
12
+ load_dotenv()
13
+
14
+ # Inicializar el modelo de lenguaje de OpenAI
15
+ llm = ChatOpenAI(
16
+ openai_api_key=getenv("OPENROUTER_API_KEY"),
17
+ openai_api_base=getenv("OPENROUTER_BASE_URL"),
18
+ model_name="openai/gpt-4o",
19
+ model_kwargs={
20
+ "extra_headers": {
21
+ "Helicone-Auth": f"Bearer " + getenv("HELICONE_API_KEY")
22
+ }
23
+ },
24
+ )
25
+
26
+ # Cambiar el directorio de trabajo
27
+ os.chdir(r"MIA/Proyecto 4")
28
+
29
+ # Cargar datos de preguntas frecuentes desde un archivo JSON
30
+ faqs_path = r"faqs.json"
31
+ with open(faqs_path, "r") as file:
32
+ faqs_data = json.load(file)
33
+
34
+ # Cargar datos del menú desde un archivo JSON
35
+ menu_path = r"menu.json"
36
+ with open(menu_path, "r") as file:
37
+ menu_data = json.load(file)
38
+
39
+ # Definir la ruta del archivo de reservas
40
+ reservas_path = r"reservas.json"
41
+
42
+ # Definir la ruta del archivo PDF del menú
43
+ menu_pdf_path = r"menu.pdf"
44
+
45
+ # Crear el archivo de reservas si no existe
46
+ if not os.path.exists(reservas_path):
47
+ with open(reservas_path, "w") as file:
48
+ json.dump([], file)
49
+
50
+ # Cargar datos de reservas desde un archivo JSON
51
+ with open(reservas_path, "r") as file:
52
+ reservas_data = json.load(file)
53
+
54
+ # Crear documentos a partir de las preguntas frecuentes
55
+ faqs_documents = [
56
+ Document(
57
+ page_content=pair["question"] + " " + pair["answer"],
58
+ metadata={"id": str(i)}
59
+ )
60
+ for i, pair in enumerate(faqs_data)
61
+ ]
62
+
63
+ # Crear documentos a partir del menú
64
+ menu_documents = [
65
+ Document(
66
+ page_content=f"{nombre}: Categoría: {info.get('categoria', 'Desconocida')}, "
67
+ f"Alérgenos: {', '.join(info.get('alergenos', [])) if info.get('alergenos') else 'Ninguno'}, "
68
+ f"Precio: ${info.get('precio', 'Desconocido')}, "
69
+ f"Descripción: {info.get('descripcion', 'Sin descripción')}, "
70
+ f"Sin Gluten: {'Sí' if info.get('sin_gluten') else 'No'}, "
71
+ f"Vegan: {'Sí' if info.get('vegan') else 'No'}",
72
+ metadata={"tipo": "menu"}
73
+ )
74
+ for nombre, info in menu_data.items()
75
+ ]
76
+
77
+ # Inicializar el modelo de embeddings de HuggingFace
78
+ embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-mpnet-base-v2")
79
+
80
+ # Inicializar la tienda de vectores Chroma
81
+ vectorstore = Chroma(embedding_function=embeddings)
82
+
83
+ # Agregar documentos de preguntas frecuentes y menú a la tienda de vectores
84
+ vectorstore.add_documents(faqs_documents)
85
+ vectorstore.add_documents(menu_documents)
86
+
87
+ # Estado de la reserva
88
+ estado_reserva = {}
89
+
90
+ # Función del chatbot
91
+ def chatbot(message, history):
92
+ global estado_reserva
93
+
94
+ message_lower = message.lower()
95
+
96
+ # Palabras clave para mostrar el menú
97
+ menu_keywords = [
98
+ "menu completo", "menú completo", "carta completa",
99
+ "ver menú", "ver menu", "mostrar menú", "mostrar menu", "quiero ver el menú",
100
+ "quiero ver la carta", "mostrar la carta", "ver la carta", "enséñame el menú",
101
+ "enséñame la carta", "dame el menú", "dame la carta", "muéstrame el menú",
102
+ "muéstrame la carta", "quiero el menú", "quiero la carta", "menu por favor",
103
+ "menú por favor", "carta por favor", "puedo ver el menú", "puedo ver la carta"
104
+ ]
105
+
106
+ # Mostrar imagen del menú si el mensaje coincide con alguna palabra clave
107
+ if any(message_lower.strip() == word for word in menu_keywords):
108
+ yield gr.Image("menu.png")
109
+ return
110
+
111
+ # Continuar con el proceso de reserva si está en proceso
112
+ if estado_reserva.get("en_proceso"):
113
+ if "dia" not in estado_reserva:
114
+ estado_reserva["dia"] = message
115
+ yield "¿A qué hora deseas la reserva? (Formato HH:MM)"
116
+ return
117
+
118
+ elif "hora" not in estado_reserva:
119
+ estado_reserva["hora"] = message
120
+ yield "¿Para cuántas personas será la reserva?"
121
+ return
122
+
123
+ elif "personas" not in estado_reserva:
124
+ estado_reserva["personas"] = message
125
+ yield "A nombre de quién será la reserva?"
126
+ return
127
+
128
+ elif "nombre" not in estado_reserva:
129
+ estado_reserva["nombre"] = message
130
+ yield "(Opcional) Proporcione un número de teléfono de contacto o escriba 'no' para omitirlo."
131
+ return
132
+
133
+ elif "telefono" not in estado_reserva:
134
+ estado_reserva["telefono"] = message if message.lower() != "no" else "No proporcionado"
135
+
136
+ # Guardar la nueva reserva en el archivo JSON
137
+ nueva_reserva = {
138
+ "nombre": estado_reserva["nombre"],
139
+ "dia": estado_reserva["dia"],
140
+ "hora": estado_reserva["hora"],
141
+ "personas": estado_reserva["personas"],
142
+ "telefono": estado_reserva["telefono"]
143
+ }
144
+
145
+ with open(reservas_path, "r") as file:
146
+ reservas_actuales = json.load(file)
147
+
148
+ reservas_actuales.append(nueva_reserva)
149
+
150
+ with open(reservas_path, "w") as file:
151
+ json.dump(reservas_actuales, file, indent=2)
152
+
153
+ estado_reserva = {}
154
+ yield f"✅ ¡Reserva guardada con éxito! Aquí están los detalles:\n{json.dumps(nueva_reserva, indent=2)}"
155
+ return
156
+
157
+ # Iniciar el proceso de reserva si se menciona en el mensaje
158
+ if "reserva" in message_lower or "quiero reservar" in message_lower:
159
+ estado_reserva["en_proceso"] = True
160
+ yield "¿Para qué día quieres hacer la reserva? (Formato DD/MM/AAAA)"
161
+ return
162
+
163
+ # Buscar documentos relevantes en la tienda de vectores
164
+ relevant_docs = vectorstore.similarity_search(message)
165
+
166
+ # Crear el contexto para el modelo de lenguaje
167
+ context_text = "\n\n".join([doc.page_content for doc in relevant_docs])
168
+
169
+ final_prompt = (
170
+ "Eres un asistente virtual en un restaurante. Puedes responder preguntas sobre el menú, las reservas y las preguntas frecuentes. "
171
+ "Si el usuario menciona restricciones dietéticas, haz recomendaciones basadas en el menú disponible.\n\n"
172
+ f"{json.dumps(menu_data, indent=2)}\n\n"
173
+ f"Contexto relevante encontrado en la base de datos:\n{context_text}\n\n"
174
+ f"Pregunta: {message}\n"
175
+ "Respuesta:"
176
+ )
177
+
178
+ # Generar la respuesta del modelo de lenguaje
179
+ messages = [{"role": "user", "content": final_prompt}]
180
+ response = llm.stream(messages)
181
+
182
+ partial_response = ""
183
+ for chunk in response:
184
+ if chunk and hasattr(chunk, "content"):
185
+ content = chunk.content
186
+ if content is not None:
187
+ partial_response += content
188
+ yield partial_response
189
+
190
+ # Configurar la interfaz de Gradio
191
+ demo = gr.ChatInterface(
192
+ chatbot,
193
+ chatbot=gr.Chatbot(height=400, type="messages"),
194
+ textbox=gr.Textbox(placeholder="Escribe tu mensaje aquí...", container=False, scale=7),
195
+ title="ChatBot Restaurante",
196
+ description="Asistente virtual para reservas, menú y preguntas frecuentes.",
197
+ theme="ocean",
198
+ examples=[
199
+ "¿Cuáles son los horarios del restaurante?",
200
+ "¿Dónde están ubicados?",
201
+ "¿Aceptan pagos con tarjeta?",
202
+ "Quiero ver la carta",
203
+ "Quiero hacer una reserva"
204
+ ],
205
+ type="messages",
206
+ editable=True,
207
+ save_history=True,
208
+ )
209
+
210
+ # Ejecutar la aplicación
211
+ if __name__ == "__main__":
212
+ demo.queue().launch()
faqs.json ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "question": "¿Cuáles son los horarios del restaurante?",
4
+ "answer": "Abrimos de 12:00 AM a 11:00 PM todos los días."
5
+ },
6
+ {
7
+ "question": "¿Dónde están ubicados?",
8
+ "answer": "Nos encontramos en Avenida Gastronómica 456, Ciudad."
9
+ },
10
+ {
11
+ "question": "¿Aceptan pagos con tarjeta?",
12
+ "answer": "Sí, aceptamos todas las tarjetas de crédito y débito, así como pagos móviles."
13
+ },
14
+ {
15
+ "question": "¿Ofrecen servicio de entrega a domicilio?",
16
+ "answer": "Sí, realizamos entregas a domicilio a través de nuestras plataformas asociadas."
17
+ },
18
+ {
19
+ "question": "¿Tienen opciones vegetarianas y veganas?",
20
+ "answer": "Sí, contamos con opciones vegetarianas y veganas en nuestro menú."
21
+ },
22
+ {
23
+ "question": "¿Tienen opciones sin gluten?",
24
+ "answer": "Sí, disponemos de opciones sin gluten. Consulte con nuestro personal para más detalles."
25
+ },
26
+ {
27
+ "question": "¿Cuentan con estacionamiento?",
28
+ "answer": "Sí, tenemos un estacionamiento privado para nuestros clientes."
29
+ },
30
+ {
31
+ "question": "¿Se pueden hacer pedidos para llevar?",
32
+ "answer": "Sí, ofrecemos servicio de comida para llevar. Puede realizar su pedido por teléfono o en persona."
33
+ },
34
+ {
35
+ "question": "¿Ofrecen menú infantil?",
36
+ "answer": "Sí, contamos con un menú especial para niños con porciones y opciones adecuadas."
37
+ },
38
+ {
39
+ "question": "¿Se necesita hacer una reserva?",
40
+ "answer": "No es necesario, pero recomendamos hacer una reserva en horarios pico o para grupos grandes."
41
+ },
42
+ {
43
+ "question": "¿Aceptan mascotas?",
44
+ "answer": "Sí, tenemos un área pet-friendly para que puedas disfrutar con tu mascota."
45
+ },
46
+ {
47
+ "question": "¿Tienen opciones sin lactosa?",
48
+ "answer": "Sí, ofrecemos opciones sin lactosa en nuestros platos y postres. Pregunta a nuestro personal."
49
+ },
50
+ {
51
+ "question": "¿Cuáles son las recomendaciones del chef?",
52
+ "answer": "Nuestras recomendaciones varían según la temporada, pero te sugerimos probar el 'Salmón a la Parrilla' y nuestra 'Pizza Margherita'."
53
+ },
54
+ {
55
+ "question": "¿Tienen menú para personas con diabetes?",
56
+ "answer": "Sí, tenemos opciones bajas en azúcar y adaptadas para personas con diabetes. Por favor, consulta con nuestro personal para más detalles."
57
+ },
58
+ {
59
+ "question": "¿Es necesario dejar propina?",
60
+ "answer": "La propina es completamente opcional, pero siempre es apreciada por nuestro equipo. Normalmente, se recomienda entre un 10% y un 15% del total de la cuenta."
61
+ },
62
+ {
63
+ "question": "¿Puedo personalizar los platos según mis preferencias?",
64
+ "answer": "Sí, estamos encantados de adaptar los platos a tus preferencias. Puedes pedir cambios en los ingredientes o ajustar los sabores."
65
+ },
66
+ {
67
+ "question": "¿Tienen algún menú especial para celebraciones?",
68
+ "answer": "Sí, ofrecemos menús especiales para celebraciones y eventos. Puedes pedir más información sobre nuestros paquetes de eventos."
69
+ },
70
+ {
71
+ "question": "¿Qué opciones de bebidas ofrecen?",
72
+ "answer": "Ofrecemos una amplia variedad de bebidas, incluyendo refrescos, jugos naturales, vinos, cervezas y cócteles."
73
+ },
74
+ {
75
+ "question": "¿Cómo puedo ponerme en contacto con ustedes?",
76
+ "answer": "Puedes contactarnos por teléfono, por email o a través de nuestras redes sociales. Todos los detalles están en nuestra página de contacto."
77
+ }
78
+ ]
menu.json ADDED
@@ -0,0 +1,147 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "Pizza Margherita": {
3
+ "categoria": "Vegetariano",
4
+ "alergenos": ["Gluten", "Lácteos"],
5
+ "precio": 12.00,
6
+ "descripcion": "Pizza clásica con salsa de tomate, mozzarella fresca y albahaca.",
7
+ "sin_gluten": true,
8
+ "vegan": false
9
+ },
10
+ "Hamburguesa Clásica": {
11
+ "categoria": "Carnes",
12
+ "alergenos": ["Gluten", "Huevo"],
13
+ "precio": 15.00,
14
+ "descripcion": "Hamburguesa de carne de res con lechuga, tomate y mayonesa.",
15
+ "sin_gluten": false,
16
+ "vegan": false
17
+ },
18
+ "Ensalada César": {
19
+ "categoria": "Ensaladas",
20
+ "alergenos": ["Lácteos", "Huevo", "Pescado"],
21
+ "precio": 10.00,
22
+ "descripcion": "Lechuga romana, pollo a la parrilla, crutones, queso parmesano y aderezo César.",
23
+ "sin_gluten": false,
24
+ "vegan": false
25
+ },
26
+ "Salmón a la Parrilla": {
27
+ "categoria": "Pescados",
28
+ "alergenos": ["Pescado"],
29
+ "precio": 18.00,
30
+ "descripcion": "Filete de salmón a la parrilla con limón y hierbas.",
31
+ "sin_gluten": true,
32
+ "vegan": false
33
+ },
34
+ "Pasta Alfredo": {
35
+ "categoria": "Pastas",
36
+ "alergenos": ["Gluten", "Lácteos"],
37
+ "precio": 14.00,
38
+ "descripcion": "Pasta con una salsa cremosa de queso parmesano y ajo.",
39
+ "sin_gluten": false,
40
+ "vegan": false
41
+ },
42
+ "Tacos de Pollo": {
43
+ "categoria": "Mexicano",
44
+ "alergenos": ["Gluten"],
45
+ "precio": 13.00,
46
+ "descripcion": "Tacos suaves de pollo con guacamole, salsa picante y cebolla.",
47
+ "sin_gluten": false,
48
+ "vegan": false
49
+ },
50
+ "Sopa de Cebolla": {
51
+ "categoria": "Sopas",
52
+ "alergenos": ["Gluten", "Lácteos"],
53
+ "precio": 9.00,
54
+ "descripcion": "Sopa caliente de cebolla caramelizada con queso gratinado.",
55
+ "sin_gluten": false,
56
+ "vegan": false
57
+ },
58
+ "Tarta de Queso": {
59
+ "categoria": "Postres",
60
+ "alergenos": ["Lácteos", "Huevo"],
61
+ "precio": 7.00,
62
+ "descripcion": "Clásica tarta de queso con base de galleta y coulis de frambuesa.",
63
+ "sin_gluten": false,
64
+ "vegan": false
65
+ },
66
+ "Jugo de Naranja Natural": {
67
+ "categoria": "Bebidas",
68
+ "alergenos": ["Ninguno"],
69
+ "precio": 5.00,
70
+ "descripcion": "Jugo recién exprimido de naranjas frescas.",
71
+ "sin_gluten": true,
72
+ "vegan": true
73
+ },
74
+ "Café Espresso": {
75
+ "categoria": "Bebidas",
76
+ "alergenos": ["Ninguno"],
77
+ "precio": 3.00,
78
+ "descripcion": "Café espresso de grano molido recién hecho.",
79
+ "sin_gluten": true,
80
+ "vegan": true
81
+ },
82
+ "Sushi Roll Especial": {
83
+ "categoria": "Sushi",
84
+ "alergenos": ["Pescado", "Soya", "Gluten"],
85
+ "precio": 20.00,
86
+ "descripcion": "Rollos de sushi con salmón, aguacate, pepino y salsa de soja.",
87
+ "sin_gluten": false,
88
+ "vegan": false
89
+ },
90
+ "Hamburguesa Vegana": {
91
+ "categoria": "Vegana",
92
+ "alergenos": ["Gluten"],
93
+ "precio": 14.00,
94
+ "descripcion": "Hamburguesa vegana con proteína de soja, lechuga, tomate y mayonesa vegana.",
95
+ "sin_gluten": false,
96
+ "vegan": true
97
+ },
98
+ "Pizza Infantil": {
99
+ "categoria": "Menú Infantil",
100
+ "alergenos": ["Gluten", "Lácteos"],
101
+ "precio": 8.00,
102
+ "descripcion": "Mini pizza con salsa de tomate, mozzarella y orégano.",
103
+ "sin_gluten": true,
104
+ "vegan": false
105
+ },
106
+ "Nuggets de Pollo": {
107
+ "categoria": "Menú Infantil",
108
+ "alergenos": ["Gluten", "Huevo"],
109
+ "precio": 7.00,
110
+ "descripcion": "Nuggets de pollo empanados acompañados de salsa de barbacoa.",
111
+ "sin_gluten": false,
112
+ "vegan": false
113
+ },
114
+ "Pasta con Salsa de Tomate": {
115
+ "categoria": "Menú Infantil",
116
+ "alergenos": ["Gluten"],
117
+ "precio": 6.00,
118
+ "descripcion": "Pasta pequeña con salsa de tomate natural y queso rallado.",
119
+ "sin_gluten": false,
120
+ "vegan": false
121
+ },
122
+ "Helado de Vainilla": {
123
+ "categoria": "Menú Infantil",
124
+ "alergenos": ["Lácteos"],
125
+ "precio": 4.00,
126
+ "descripcion": "Helado cremoso de vainilla, servido con sirope de chocolate.",
127
+ "sin_gluten": true,
128
+ "vegan": false
129
+ },
130
+ "Limonada Natural": {
131
+ "categoria": "Menú Infantil",
132
+ "alergenos": ["Ninguno"],
133
+ "precio": 3.00,
134
+ "descripcion": "Limonada casera con agua, limón fresco y un toque de azúcar.",
135
+ "sin_gluten": true,
136
+ "vegan": true
137
+
138
+ },
139
+ "Brownie de Chocolate": {
140
+ "categoria": "Postres",
141
+ "alergenos": ["Gluten", "Lácteos", "Nueces"],
142
+ "precio": 6.00,
143
+ "descripcion": "Brownie de chocolate casero con nueces y sirope de chocolate.",
144
+ "sin_gluten": false,
145
+ "vegan": false
146
+ }
147
+ }
menu.png ADDED

Git LFS Details

  • SHA256: 1cc542da9229faffea386f46bb53a285965640e92fed2549136e85df4471a400
  • Pointer size: 132 Bytes
  • Size of remote file: 2.42 MB
reservas.json ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "nombre": "Juan Fernandez",
4
+ "dia": "12/02/2025",
5
+ "hora": "21:00",
6
+ "personas": "10",
7
+ "telefono": "642128722"
8
+ },
9
+ {
10
+ "nombre": "Manuel",
11
+ "dia": "20/05/2025",
12
+ "hora": "20:05",
13
+ "personas": "5",
14
+ "telefono": "No proporcionado"
15
+ },
16
+ {
17
+ "nombre": "Pedro",
18
+ "dia": "20/05/2025",
19
+ "hora": "20:30",
20
+ "personas": "5",
21
+ "telefono": "No proporcionado"
22
+ },
23
+ {
24
+ "nombre": "David",
25
+ "dia": "20/03/2025",
26
+ "hora": "20:34",
27
+ "personas": "8",
28
+ "telefono": "No proporcionado"
29
+ },
30
+ {
31
+ "nombre": "David",
32
+ "dia": "20/03/2025",
33
+ "hora": "20:35",
34
+ "personas": "5",
35
+ "telefono": "No proporcionado"
36
+ },
37
+ {
38
+ "nombre": "David",
39
+ "dia": "20/05/2025",
40
+ "hora": "20:35",
41
+ "personas": "5",
42
+ "telefono": "No proporcionado"
43
+ }
44
+ ]