Update app.py
Browse files
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 |
-
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 1167 |
-
|
| 1168 |
-
|
| 1169 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1170 |
|
| 1171 |
async def _complete_task_gradio(user_obj, task_name):
|
| 1172 |
-
|
| 1173 |
-
|
| 1174 |
-
|
| 1175 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 1200 |
-
|
| 1201 |
-
|
| 1202 |
-
|
| 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 |
-
#
|
| 1259 |
-
#
|
| 1260 |
-
|
| 1261 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1262 |
hidden_voice_submit_button.click(
|
| 1263 |
-
fn=
|
| 1264 |
-
inputs=[voice_input_textbox],
|
| 1265 |
-
outputs=[
|
| 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,
|