Lukeetah commited on
Commit
eadeba9
·
verified ·
1 Parent(s): d66429b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +222 -60
app.py CHANGED
@@ -597,6 +597,8 @@ with demo: # Ahora el bloque 'with' utiliza el objeto demo ya creado
597
  # gr.State para mantener el objeto User en la sesión y el estado de la UI
598
  current_user_state = gr.State(None)
599
  current_view_state = gr.State("home") # Controla qué sección de la UI está visible
 
 
600
 
601
  gr.Markdown(
602
  """
@@ -604,7 +606,7 @@ with demo: # Ahora el bloque 'with' utiliza el objeto demo ya creado
604
  🧉 MateAI: El Oráculo Vocal y Adaptativo del Bienestar Argento 🧉
605
  </h1>
606
  <p style="text-align: center; color: #4b5563; font-size: 1.3em; margin-bottom: 2em; line-height: 1.5;">
607
- Una inteligencia superior diseñada para cebarte la vida con sabiduría contextual y sin costo.
608
  <br><b>¡Sin APIs de pago, sin consumo de tokens!</b> Solo buena onda, insights profundos y el "sabor" de casa.
609
  <br><i>La IA que se integra a la humanidad, comprendiendo tu mundo.</i>
610
  </p>
@@ -784,7 +786,7 @@ with demo: # Ahora el bloque 'with' utiliza el objeto demo ya creado
784
  voiceStatus.textContent = 'No se detectó voz. Intenta hablar más claro o más fuerte. 🎤';
785
  voiceStatus.style.color = '#fbbf24'; // Amber
786
  } else if (event.error === 'aborted') {
787
- voiceStatus.textContent = 'Reconocimiento de voz cancelado. 🎤';
788
  voiceStatus.style.color = '#4b5563';
789
  }
790
  };
@@ -804,8 +806,9 @@ with demo: # Ahora el bloque 'with' utiliza el objeto demo ya creado
804
  const voices = window.speechSynthesis.getVoices();
805
  // Prioriza voces de Argentina, luego cualquier español, luego la por defecto
806
  // Se busca una voz que suene más "argentina" por su nombre o lang code
807
- const preferredVoice = voices.find(voice => voice.lang === 'es-AR' && (voice.name.includes('Argentina') || voice.name.includes('Diego') || voice.name.includes('Laura'))) ||
808
  voices.find(voice => voice.lang === 'es-AR') ||
 
809
  voices.find(voice => voice.lang.startsWith('es'));
810
  if (preferredVoice) {
811
  utterance.voice = preferredVoice;
@@ -839,7 +842,7 @@ with demo: # Ahora el bloque 'with' utiliza el objeto demo ya creado
839
  utterance.onerror = function(event) {
840
  speaking = false;
841
  console.error('Speech synthesis error:', event.error);
842
- voiceStatus.textContent = 'Error al hablar ';
843
  voiceStatus.style.color = '#ef4444'; // Red
844
  matecitoAvatar.classList.remove('matecito-talking');
845
  matecitoAvatar.style.animation = 'fade-out 0.3s forwards';
@@ -880,7 +883,18 @@ with demo: # Ahora el bloque 'with' utiliza el objeto demo ya creado
880
  btn_visual_assistant = gr.Button("<div class='icon'>👁️</div>Asistente Visual (Concepto)", elem_classes="menu-button", value="visual_assistant")
881
 
882
  # --- Contenido de cada Sección (Ahora en gr.Group o gr.Column) ---
883
- with gr.Column(elem_id="home_section", elem_classes="section-content"):
 
 
 
 
 
 
 
 
 
 
 
884
  gr.Markdown("<h2 style='color: #10b981;'>¡Bienvenido a tu Espacio MateAI!</h2><p>Acá manejás tu perfil para que MateAI te conozca a fondo.</p>")
885
  with gr.Row():
886
  with gr.Column():
@@ -936,40 +950,6 @@ with demo: # Ahora el bloque 'with' utiliza el objeto demo ya creado
936
  btn_oraculo = gr.Button("¡Consultar al Oráculo MateAI!", variant="primary")
937
  gr.Markdown("<p style='text-align: center; font-size: 0.9em; color: #6b7280; margin-top: 1em;'><i>El Oráculo te hablará una vez por día. Volvé mañana para una nueva revelación.</i></p>")
938
 
939
- with gr.Column(elem_id="nudges_section", elem_classes="section-content", visible=False):
940
- gr.Markdown("<h2 style='color: #10b981;'>¡Pedí un Susurro Contextual a MateAI!</h2><p>Definí tu contexto y dejá que MateAI te sorprenda.</p>")
941
- with gr.Row():
942
- hora_del_dia_input = gr.Dropdown(
943
- ["Mañana", "Mediodía/Tarde", "Noche"],
944
- label="Momento del Día",
945
- value="Mañana"
946
- )
947
- ubicacion_input = gr.Dropdown(
948
- ["Casa", "Oficina/Estudio", "Aire Libre/Calle"],
949
- label="¿Dónde estás?",
950
- value="Casa"
951
- )
952
- with gr.Row():
953
- actividad_input = gr.Dropdown(
954
- ["Relajado", "Trabajando", "Ejercicio", "Leyendo", "Cocinando", "Viendo un partido"],
955
- label="¿En qué andás?",
956
- value="Relajado"
957
- )
958
- estado_animo_input = gr.Dropdown(
959
- ["Bien", "Cansado", "Bajoneado", "Motivado"],
960
- label="¿Cómo te sentís?",
961
- value="Bien"
962
- )
963
-
964
- btn_generar_susurro = gr.Button("¡Che, MateAI, tirame un susurro!", variant="primary")
965
- susurro_output = gr.Textbox(
966
- label="Tu Susurro de Bienestar y Sostenibilidad",
967
- lines=5,
968
- placeholder="MateAI está esperando tu contexto para cebarte un susurro...",
969
- interactive=False
970
- )
971
- gr.Markdown(f"<p style='font-size: 0.9em; color: #6b7280; margin-top: 1em;'><i>MateAI aplica un pequeño 'cooldown' de {Config.NUDGE_COOLDOWN_MINUTES} minuto(s) para que los susurros sean más valiosos.</i></p>")
972
-
973
  with gr.Column(elem_id="challenges_section", elem_classes="section-content", visible=False):
974
  gr.Markdown("<h2 style='text-align: center; color: #10b981;'>🎯 Desafíos MateAI: ¡Ponete a Prueba! 🎯</h2><p style='text-align: center;'>MateAI te propone desafíos para crecer en bienestar y sostenibilidad. ¡Aceptá la misión!</p>")
975
  desafio_output = gr.Textbox(
@@ -1163,20 +1143,190 @@ with demo: # Ahora el bloque 'with' utiliza el objeto demo ya creado
1163
  return challenge_text, challenge_text # Retorna el texto para vocalizar
1164
 
1165
  async def _add_task_gradio(user_obj, task_name):
1166
- # Asegurarse de que user_obj no sea None antes de intentar acceder a user_obj.user_id
1167
- user_id = user_obj.user_id if user_obj else None
1168
- msg, tareas_str = await nudge_generator.add_task(user_id, task_name)
1169
- return msg, tareas_str
 
 
 
 
 
 
 
1170
 
1171
  async def _complete_task_gradio(user_obj, task_name):
1172
- # Asegurarse de que user_obj no sea None antes de intentar acceder a user_obj.user_id
1173
- user_id = user_obj.user_id if user_obj else None
1174
- msg, tareas_str = await nudge_generator.complete_task(user_id, task_name)
1175
- return msg, tareas_str
 
 
 
 
 
 
 
 
 
 
 
1176
 
1177
  def _download_diary_gradio(diary_text):
1178
  return gr.File(value=diary_text.encode('utf-8'), filename="diario_mateai.txt", type="bytes")
1179
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1180
  # --- Conexión de Eventos ---
1181
  btn_crear_usuario.click(
1182
  fn=_create_user_gradio,
@@ -1196,11 +1346,13 @@ with demo: # Ahora el bloque 'with' utiliza el objeto demo ya creado
1196
  outputs=[output_actualizar_preferencias, current_user_state]
1197
  )
1198
 
1199
- btn_generar_susurro.click(
1200
- fn=_generate_nudge_gradio,
1201
- inputs=[current_user_state, ubicacion_input, actividad_input, estado_animo_input],
1202
- outputs=[susurro_output, usuario_actual_puntos, usuario_actual_insignia, usuario_proxima_insignia, historial_susurros_output, voice_output_text]
1203
- )
 
 
1204
 
1205
  btn_oraculo.click(
1206
  fn=_get_oracle_revelation_gradio,
@@ -1254,15 +1406,25 @@ with demo: # Ahora el bloque 'with' utiliza el objeto demo ya creado
1254
  outputs=[simulated_action_output, voice_output_text]
1255
  )
1256
 
1257
- # --- Manejo de la entrada de voz ---
1258
- # Cuando el texto es reconocido por JS y puesto en voice_input_textbox,
1259
- # este evento lo envía al backend para procesamiento.
1260
- # NOTA: En una implementación completa de comandos de voz, se necesitaría un parser de lenguaje natural
1261
- # para mapear el texto reconocido a las funciones de Gradio. Aquí, se muestra un mensaje de ejemplo.
 
 
 
 
 
 
 
 
 
 
1262
  hidden_voice_submit_button.click(
1263
- fn=lambda text: f"Comando de voz recibido: '{text}'. Por favor, usa los botones o campos para interactuar directamente. ¡MateAI aún está aprendiendo a entender tus comandos hablados complejos!", # Mensaje temporal
1264
- inputs=[voice_input_textbox],
1265
- outputs=[susurro_output] # Solo para mostrar que se recibió el comando
1266
  )
1267
 
1268
  # Cuando el backend genera texto para voz, lo envía a voice_output_text,
 
597
  # gr.State para mantener el objeto User en la sesión y el estado de la UI
598
  current_user_state = gr.State(None)
599
  current_view_state = gr.State("home") # Controla qué sección de la UI está visible
600
+ # Estado para la conversación contextual (para ir pidiendo datos)
601
+ conversation_context_state = gr.State({"step": "initial", "pending_nudge_request": False, "location": None, "activity": None, "sentiment": None})
602
 
603
  gr.Markdown(
604
  """
 
606
  🧉 MateAI: El Oráculo Vocal y Adaptativo del Bienestar Argento 🧉
607
  </h1>
608
  <p style="text-align: center; color: #4b5563; font-size: 1.3em; margin-bottom: 2em; line-height: 1.5;">
609
+ Soy MateAI, tu compañero argentino. Estoy acá para cebarte la vida con sabiduría contextual y sin costo.
610
  <br><b>¡Sin APIs de pago, sin consumo de tokens!</b> Solo buena onda, insights profundos y el "sabor" de casa.
611
  <br><i>La IA que se integra a la humanidad, comprendiendo tu mundo.</i>
612
  </p>
 
786
  voiceStatus.textContent = 'No se detectó voz. Intenta hablar más claro o más fuerte. 🎤';
787
  voiceStatus.style.color = '#fbbf24'; // Amber
788
  } else if (event.error === 'aborted') {
789
+ voiceStatus.textContent = 'Reconocimiento de voz cancelado. ';
790
  voiceStatus.style.color = '#4b5563';
791
  }
792
  };
 
806
  const voices = window.speechSynthesis.getVoices();
807
  // Prioriza voces de Argentina, luego cualquier español, luego la por defecto
808
  // Se busca una voz que suene más "argentina" por su nombre o lang code
809
+ const preferredVoice = voices.find(voice => voice.lang === 'es-AR' && (voice.name.includes('Argentina') || voice.name.includes('Diego') || voice.name.includes('Laura') || voice.name.includes('Argentine') || voice.name.includes('male'))) ||
810
  voices.find(voice => voice.lang === 'es-AR') ||
811
+ voices.find(voice => voice.lang.startsWith('es') && voice.name.includes('male')) ||
812
  voices.find(voice => voice.lang.startsWith('es'));
813
  if (preferredVoice) {
814
  utterance.voice = preferredVoice;
 
842
  utterance.onerror = function(event) {
843
  speaking = false;
844
  console.error('Speech synthesis error:', event.error);
845
+ voiceStatus.textContent = 'Error al hablar 🔇';
846
  voiceStatus.style.color = '#ef4444'; // Red
847
  matecitoAvatar.classList.remove('matecito-talking');
848
  matecitoAvatar.style.animation = 'fade-out 0.3s forwards';
 
883
  btn_visual_assistant = gr.Button("<div class='icon'>👁️</div>Asistente Visual (Concepto)", elem_classes="menu-button", value="visual_assistant")
884
 
885
  # --- Contenido de cada Sección (Ahora en gr.Group o gr.Column) ---
886
+ # La sección de susurros ahora es un chat
887
+ with gr.Column(elem_id="nudges_section", elem_classes="section-content", visible=False):
888
+ gr.Markdown("<h2 style='color: #10b981;'>¡Che, acá estoy para cebarte un susurro!</h2><p>Contame un poco de tu día y te tiro la posta.</p>")
889
+ chat_history = gr.State([]) # Para mantener el historial del chat
890
+ chatbot = gr.Chatbot(label="Conversación con MateAI", height=300)
891
+ msg_input = gr.Textbox(label="Contale a MateAI...", placeholder="Ej: 'Estoy en casa, laburando y medio cansado.' o 'Quiero un susurro de bienestar.'")
892
+
893
+ # Botón para enviar el mensaje de texto (además de la entrada de voz)
894
+ btn_send_msg = gr.Button("Enviar Mensaje", variant="primary")
895
+
896
+ # Resto de las secciones (ocultas por defecto)
897
+ with gr.Column(elem_id="home_section", elem_classes="section-content", visible=True): # Home visible por defecto
898
  gr.Markdown("<h2 style='color: #10b981;'>¡Bienvenido a tu Espacio MateAI!</h2><p>Acá manejás tu perfil para que MateAI te conozca a fondo.</p>")
899
  with gr.Row():
900
  with gr.Column():
 
950
  btn_oraculo = gr.Button("¡Consultar al Oráculo MateAI!", variant="primary")
951
  gr.Markdown("<p style='text-align: center; font-size: 0.9em; color: #6b7280; margin-top: 1em;'><i>El Oráculo te hablará una vez por día. Volvé mañana para una nueva revelación.</i></p>")
952
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
953
  with gr.Column(elem_id="challenges_section", elem_classes="section-content", visible=False):
954
  gr.Markdown("<h2 style='text-align: center; color: #10b981;'>🎯 Desafíos MateAI: ¡Ponete a Prueba! 🎯</h2><p style='text-align: center;'>MateAI te propone desafíos para crecer en bienestar y sostenibilidad. ¡Aceptá la misión!</p>")
955
  desafio_output = gr.Textbox(
 
1143
  return challenge_text, challenge_text # Retorna el texto para vocalizar
1144
 
1145
  async def _add_task_gradio(user_obj, task_name):
1146
+ if not user_id: # Check if user_id is None or empty
1147
+ return "Error: Por favor, crea o carga un usuario primero.", "" # Return default values for outputs
1148
+ user = await self.user_manager.get_user(user_id)
1149
+ if not user:
1150
+ return "Error: Usuario no logueado. Por favor, crea o carga un usuario.", ""
1151
+
1152
+ new_task = {"task": task_name, "added_at": datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f'), "last_nudged": None}
1153
+ user.tasks.append(new_task)
1154
+ await self.user_manager.update_user_data(user)
1155
+ tareas_str = "\n".join([f"- {t['task']} (Agregada el: {datetime.strptime(t['added_at'], '%Y-%m-%d %H:%M:%S.%f').strftime('%d/%m')})" for t in user.tasks])
1156
+ return f"¡Tarea '{task_name}' agregada a tu lista, {user.name}!", tareas_str
1157
 
1158
  async def _complete_task_gradio(user_obj, task_name):
1159
+ if not user_id: # Check if user_id is None or empty
1160
+ return "Error: Por favor, crea o carga un usuario primero.", "" # Return default values for outputs
1161
+ user = await self.user_manager.get_user(user_id)
1162
+ if not user:
1163
+ return "Error: Usuario no logueado. Por favor, crea o carga un usuario.", ""
1164
+
1165
+ initial_task_count = len(user.tasks)
1166
+ user.tasks = [task for task in user.tasks if task['task'].lower() != task_name.lower()]
1167
+
1168
+ if len(user.tasks) < initial_task_count:
1169
+ await self.user_manager.update_user_data(user)
1170
+ tareas_str = "\n".join([f"- {t['task']} (Agregada el: {datetime.strptime(t['added_at'], '%Y-%m-%d %H:%M:%S.%f').strftime('%d/%m')})" for t in user.tasks])
1171
+ return f"¡Tarea '{task_name}' marcada como completada, {user.name}! ¡Bien ahí!", tareas_str
1172
+ tareas_str = "\n".join([f"- {t['task']} (Agregada el: {datetime.strptime(t['added_at'], '%Y-%m-%d %H:%M:%S.%f').strftime('%d/%m')})" for t in user.tasks])
1173
+ return f"No encontré la tarea '{task_name}' en tu lista, {user.name}.", tareas_str
1174
 
1175
  def _download_diary_gradio(diary_text):
1176
  return gr.File(value=diary_text.encode('utf-8'), filename="diario_mateai.txt", type="bytes")
1177
 
1178
+ # --- Funciones para el chat conversacional ---
1179
+ async def _process_conversational_input(user_message, chat_history, current_user_state, conversation_context_state):
1180
+ user_message_lower = user_message.lower()
1181
+ response = ""
1182
+ user_obj = current_user_state
1183
+
1184
+ if not user_obj:
1185
+ chat_history.append((user_message, "¡Che, para arrancar, necesito que crees o cargues tu perfil en la sección 'Inicio & Perfil'! Después volvé acá y seguimos charlando."))
1186
+ return chat_history, "", conversation_context_state, ""
1187
+
1188
+ # Manejo de comandos directos para otras secciones (simula la voz controlando el menú)
1189
+ if "oráculo" in user_message_lower or "revelación" in user_message_lower:
1190
+ revelation_text = await nudge_generator.get_daily_oracle_revelation(user_obj.user_id)
1191
+ chat_history.append((user_message, revelation_text))
1192
+ return chat_history, "", conversation_context_state, revelation_text
1193
+
1194
+ if "desafío" in user_message_lower or "reto" in user_message_lower:
1195
+ challenge_text = await nudge_generator.get_mateai_challenge(user_obj.user_id)
1196
+ chat_history.append((user_message, challenge_text))
1197
+ return chat_history, "", conversation_context_state, challenge_text
1198
+
1199
+ if "agregar tarea" in user_message_lower or "nueva tarea" in user_message_lower:
1200
+ task_name = user_message_lower.replace("agregar tarea", "").replace("nueva tarea", "").strip()
1201
+ if task_name:
1202
+ msg, _ = await nudge_generator.add_task(user_obj.user_id, task_name)
1203
+ chat_history.append((user_message, msg))
1204
+ return chat_history, "", conversation_context_state, msg
1205
+ else:
1206
+ chat_history.append((user_message, "¡Dale, che! ¿Qué tarea querés que agregue? Decime, por ejemplo: 'agregar tarea comprar yerba'."))
1207
+ return chat_history, "", conversation_context_state, "¡Dale, che! ¿Qué tarea querés que agregue? Decime, por ejemplo: 'agregar tarea comprar yerba'."
1208
+
1209
+ if "completar tarea" in user_message_lower or "tarea lista" in user_message_lower:
1210
+ task_name = user_message_lower.replace("completar tarea", "").replace("tarea lista", "").strip()
1211
+ if task_name:
1212
+ msg, _ = await nudge_generator.complete_task(user_obj.user_id, task_name)
1213
+ chat_history.append((user_message, msg))
1214
+ return chat_history, "", conversation_context_state, msg
1215
+ else:
1216
+ chat_history.append((user_message, "¡Decime qué tarea completaste, así la saco de la lista! Por ejemplo: 'completar tarea ir al súper'."))
1217
+ return chat_history, "", conversation_context_state, "¡Decime qué tarea completaste, así la saco de la lista! Por ejemplo: 'completar tarea ir al súper'."
1218
+
1219
+ # Lógica para pedir un susurro contextual
1220
+ current_step = conversation_context_state["step"]
1221
+ location = conversation_context_state["location"]
1222
+ activity = conversation_context_state["activity"]
1223
+ sentiment = conversation_context_state["sentiment"]
1224
+ pending_nudge_request = conversation_context_state["pending_nudge_request"]
1225
+
1226
+ if "susurro" in user_message_lower or "decime algo" in user_message_lower or "onda" in user_message_lower or "che" in user_message_lower:
1227
+ pending_nudge_request = True
1228
+ conversation_context_state["pending_nudge_request"] = True
1229
+ if not location:
1230
+ response = "¡Dale! Para cebarte un susurro a medida, contame, ¿dónde estás ahora? ¿En casa, en la oficina, en la calle?"
1231
+ conversation_context_state["step"] = "ask_location"
1232
+ elif not activity:
1233
+ response = f"Ah, estás en {location}. ¿Y en qué andás ahora? ¿Laburando, haciendo ejercicio, relajado, cocinando, viendo un partido?"
1234
+ conversation_context_state["step"] = "ask_activity"
1235
+ elif not sentiment:
1236
+ response = f"Entendido, en {location} y {activity}. ¿Y cómo te sentís? ¿Bien, cansado, bajoneado, motivado?"
1237
+ conversation_context_state["step"] = "ask_sentiment"
1238
+ else:
1239
+ # Ya tenemos todo, generar el nudge
1240
+ nudge_text, current_points, current_insignia, next_insignia_goal, historial = await nudge_generator.generate_nudge(user_obj.user_id, location, activity, sentiment)
1241
+ response = nudge_text
1242
+ chat_history.append((user_message, response))
1243
+ # Reiniciar contexto después de generar el nudge
1244
+ conversation_context_state = {"step": "initial", "pending_nudge_request": False, "location": None, "activity": None, "sentiment": None}
1245
+ return chat_history, "", conversation_context_state, response
1246
+
1247
+ elif current_step == "ask_location":
1248
+ if any(k in user_message_lower for k in ["casa", "hogar"]):
1249
+ location = "Casa"
1250
+ elif any(k in user_message_lower for k in ["oficina", "laburo", "estudio", "trabajando"]):
1251
+ location = "Oficina/Estudio"
1252
+ elif any(k in user_message_lower for k in ["calle", "aire libre", "plaza", "parque", "caminando"]):
1253
+ location = "Aire Libre/Calle"
1254
+ else:
1255
+ chat_history.append((user_message, "No te entendí bien, che. ¿Estás en casa, en la oficina o en la calle?"))
1256
+ return chat_history, "", conversation_context_state, ""
1257
+
1258
+ conversation_context_state["location"] = location
1259
+ if not activity:
1260
+ response = f"Ah, estás en {location}. ¿Y en qué andás ahora? ¿Laburando, haciendo ejercicio, relajado, cocinando, viendo un partido?"
1261
+ conversation_context_state["step"] = "ask_activity"
1262
+ elif not sentiment:
1263
+ response = f"Entendido, en {location} y {activity}. ¿Y cómo te sentís? ¿Bien, cansado, bajoneado, motivado?"
1264
+ conversation_context_state["step"] = "ask_sentiment"
1265
+ else: # Should not happen if flow is sequential, but as fallback
1266
+ nudge_text, current_points, current_insignia, next_insignia_goal, historial = await nudge_generator.generate_nudge(user_obj.user_id, location, activity, sentiment)
1267
+ response = nudge_text
1268
+ chat_history.append((user_message, response))
1269
+ conversation_context_state = {"step": "initial", "pending_nudge_request": False, "location": None, "activity": None, "sentiment": None}
1270
+ return chat_history, "", conversation_context_state, response
1271
+
1272
+ elif current_step == "ask_activity":
1273
+ if any(k in user_message_lower for k in ["relajado", "tranqui", "descansando"]):
1274
+ activity = "Relajado"
1275
+ elif any(k in user_message_lower for k in ["laburando", "trabajando", "estudiando"]):
1276
+ activity = "Trabajando"
1277
+ elif any(k in user_message_lower for k in ["ejercicio", "entrenando", "corriendo", "caminando"]):
1278
+ activity = "Ejercicio"
1279
+ elif any(k in user_message_lower for k in ["leyendo", "libro"]):
1280
+ activity = "Leyendo"
1281
+ elif any(k in user_message_lower for k in ["cocinando", "comida"]):
1282
+ activity = "Cocinando"
1283
+ elif any(k in user_message_lower for k in ["partido", "fútbol", "viendo tele"]):
1284
+ activity = "Viendo un partido"
1285
+ else:
1286
+ chat_history.append((user_message, "No te pesqué esa, che. ¿Estás laburando, haciendo ejercicio, relajado, cocinando, o viendo un partido?"))
1287
+ return chat_history, "", conversation_context_state, ""
1288
+
1289
+ conversation_context_state["activity"] = activity
1290
+ if not sentiment:
1291
+ response = f"Entendido, en {location} y {activity}. ¿Y cómo te sentís? ¿Bien, cansado, bajoneado, motivado?"
1292
+ conversation_context_state["step"] = "ask_sentiment"
1293
+ else: # Should not happen if flow is sequential, but as fallback
1294
+ nudge_text, current_points, current_insignia, next_insignia_goal, historial = await nudge_generator.generate_nudge(user_obj.user_id, location, activity, sentiment)
1295
+ response = nudge_text
1296
+ chat_history.append((user_message, response))
1297
+ conversation_context_state = {"step": "initial", "pending_nudge_request": False, "location": None, "activity": None, "sentiment": None}
1298
+ return chat_history, "", conversation_context_state, response
1299
+
1300
+ elif current_step == "ask_sentiment":
1301
+ if any(k in user_message_lower for k in ["bien", "joya", "diez puntos", "contento"]):
1302
+ sentiment = "Bien"
1303
+ elif any(k in user_message_lower for k in ["cansado", "agotado", "fundido"]):
1304
+ sentiment = "Cansado"
1305
+ elif any(k in user_message_lower for k in ["bajoneado", "triste", "mal", "depre"]):
1306
+ sentiment = "Bajoneado"
1307
+ elif any(k in user_message_lower for k in ["motivado", "con pilas", "enchufado"]):
1308
+ sentiment = "Motivado"
1309
+ else:
1310
+ chat_history.append((user_message, "No te capto el sentimiento, che. ¿Estás bien, cansado, bajoneado o motivado?"))
1311
+ return chat_history, "", conversation_context_state, ""
1312
+
1313
+ conversation_context_state["sentiment"] = sentiment
1314
+ # Ahora que tenemos todo, generar el nudge
1315
+ nudge_text, current_points, current_insignia, next_insignia_goal, historial = await nudge_generator.generate_nudge(user_obj.user_id, location, activity, sentiment)
1316
+ response = nudge_text
1317
+ chat_history.append((user_message, response))
1318
+ # Reiniciar contexto después de generar el nudge
1319
+ conversation_context_state = {"step": "initial", "pending_nudge_request": False, "location": None, "activity": None, "sentiment": None}
1320
+ return chat_history, "", conversation_context_state, response
1321
+
1322
+ else: # Default response if no specific command or context step
1323
+ response = "¡No te entendí bien, che! Soy MateAI, tu compañero. Si querés un susurro, decime 'quiero un susurro' y te voy a preguntar unas cositas. Si no, decime qué andás necesitando."
1324
+ chat_history.append((user_message, response))
1325
+ return chat_history, "", conversation_context_state, response
1326
+
1327
+ chat_history.append((user_message, response))
1328
+ return chat_history, "", conversation_context_state, response
1329
+
1330
  # --- Conexión de Eventos ---
1331
  btn_crear_usuario.click(
1332
  fn=_create_user_gradio,
 
1346
  outputs=[output_actualizar_preferencias, current_user_state]
1347
  )
1348
 
1349
+ # El botón de generar susurro en la sección de susurros ya no se usa directamente para generar,
1350
+ # sino que se activa a través de la conversación.
1351
+ # btn_generar_susurro.click(
1352
+ # fn=_generate_nudge_gradio,
1353
+ # inputs=[current_user_state, ubicacion_input, actividad_input, estado_animo_input],
1354
+ # outputs=[susurro_output, usuario_actual_puntos, usuario_actual_insignia, usuario_proxima_insignia, historial_susurros_output, voice_output_text]
1355
+ # )
1356
 
1357
  btn_oraculo.click(
1358
  fn=_get_oracle_revelation_gradio,
 
1406
  outputs=[simulated_action_output, voice_output_text]
1407
  )
1408
 
1409
+ # --- Manejo de la entrada de voz y texto conversacional ---
1410
+ # El input de texto (msg_input) y el botón de enviar (btn_send_msg)
1411
+ # ahora se conectan a la función _process_conversational_input
1412
+ msg_input.submit(
1413
+ fn=_process_conversational_input,
1414
+ inputs=[msg_input, chat_history, current_user_state, conversation_context_state],
1415
+ outputs=[chatbot, msg_input, conversation_context_state, voice_output_text]
1416
+ )
1417
+ btn_send_msg.click(
1418
+ fn=_process_conversational_input,
1419
+ inputs=[msg_input, chat_history, current_user_state, conversation_context_state],
1420
+ outputs=[chatbot, msg_input, conversation_context_state, voice_output_text]
1421
+ )
1422
+
1423
+ # El botón oculto para la entrada de voz también se conecta a la función conversacional
1424
  hidden_voice_submit_button.click(
1425
+ fn=_process_conversational_input,
1426
+ inputs=[voice_input_textbox, chat_history, current_user_state, conversation_context_state],
1427
+ outputs=[chatbot, voice_input_textbox, conversation_context_state, voice_output_text]
1428
  )
1429
 
1430
  # Cuando el backend genera texto para voz, lo envía a voice_output_text,