Browse Source

wdym i can save the record twice lmao

Matthew Trejo 1 month ago
parent
commit
95d7c2a277
5 changed files with 313 additions and 115 deletions
  1. 124 0
      package-lock.json
  2. 1 0
      package.json
  3. 70 57
      src/app/api/chat/route.ts
  4. 16 0
      src/hooks/useChat.ts
  5. 102 58
      src/lib/chat-prompts.ts

+ 124 - 0
package-lock.json

@@ -26,6 +26,7 @@
         "@radix-ui/react-switch": "^1.2.6",
         "@radix-ui/react-tabs": "^1.1.13",
         "@radix-ui/react-toast": "^1.2.14",
+        "@radix-ui/react-tooltip": "^1.2.8",
         "@react-pdf/renderer": "^4.3.0",
         "@types/bcryptjs": "^2.4.6",
         "ai": "^4.3.19",
@@ -3606,6 +3607,129 @@
         }
       }
     },
+    "node_modules/@radix-ui/react-tooltip": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.8.tgz",
+      "integrity": "sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==",
+      "license": "MIT",
+      "dependencies": {
+        "@radix-ui/primitive": "1.1.3",
+        "@radix-ui/react-compose-refs": "1.1.2",
+        "@radix-ui/react-context": "1.1.2",
+        "@radix-ui/react-dismissable-layer": "1.1.11",
+        "@radix-ui/react-id": "1.1.1",
+        "@radix-ui/react-popper": "1.2.8",
+        "@radix-ui/react-portal": "1.1.9",
+        "@radix-ui/react-presence": "1.1.5",
+        "@radix-ui/react-primitive": "2.1.3",
+        "@radix-ui/react-slot": "1.2.3",
+        "@radix-ui/react-use-controllable-state": "1.2.2",
+        "@radix-ui/react-visually-hidden": "1.2.3"
+      },
+      "peerDependencies": {
+        "@types/react": "*",
+        "@types/react-dom": "*",
+        "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+        "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        },
+        "@types/react-dom": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/primitive": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz",
+      "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==",
+      "license": "MIT"
+    },
+    "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-dismissable-layer": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz",
+      "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==",
+      "license": "MIT",
+      "dependencies": {
+        "@radix-ui/primitive": "1.1.3",
+        "@radix-ui/react-compose-refs": "1.1.2",
+        "@radix-ui/react-primitive": "2.1.3",
+        "@radix-ui/react-use-callback-ref": "1.1.1",
+        "@radix-ui/react-use-escape-keydown": "1.1.1"
+      },
+      "peerDependencies": {
+        "@types/react": "*",
+        "@types/react-dom": "*",
+        "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+        "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        },
+        "@types/react-dom": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-popper": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz",
+      "integrity": "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==",
+      "license": "MIT",
+      "dependencies": {
+        "@floating-ui/react-dom": "^2.0.0",
+        "@radix-ui/react-arrow": "1.1.7",
+        "@radix-ui/react-compose-refs": "1.1.2",
+        "@radix-ui/react-context": "1.1.2",
+        "@radix-ui/react-primitive": "2.1.3",
+        "@radix-ui/react-use-callback-ref": "1.1.1",
+        "@radix-ui/react-use-layout-effect": "1.1.1",
+        "@radix-ui/react-use-rect": "1.1.1",
+        "@radix-ui/react-use-size": "1.1.1",
+        "@radix-ui/rect": "1.1.1"
+      },
+      "peerDependencies": {
+        "@types/react": "*",
+        "@types/react-dom": "*",
+        "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+        "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        },
+        "@types/react-dom": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-presence": {
+      "version": "1.1.5",
+      "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz",
+      "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@radix-ui/react-compose-refs": "1.1.2",
+        "@radix-ui/react-use-layout-effect": "1.1.1"
+      },
+      "peerDependencies": {
+        "@types/react": "*",
+        "@types/react-dom": "*",
+        "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+        "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        },
+        "@types/react-dom": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/@radix-ui/react-use-callback-ref": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz",

+ 1 - 0
package.json

@@ -41,6 +41,7 @@
     "@radix-ui/react-switch": "^1.2.6",
     "@radix-ui/react-tabs": "^1.1.13",
     "@radix-ui/react-toast": "^1.2.14",
+    "@radix-ui/react-tooltip": "^1.2.8",
     "@react-pdf/renderer": "^4.3.0",
     "@types/bcryptjs": "^2.4.6",
     "ai": "^4.3.19",

+ 70 - 57
src/app/api/chat/route.ts

@@ -107,6 +107,18 @@ export async function POST(request: NextRequest) {
 
       const systemPrompt = getSystemPrompt(chatType as "medical" | "psychological");
 
+      // Agregar recordatorio de formato al último mensaje si es del usuario
+      const messagesWithReminder = [...conversationHistory];
+      const lastMessage = messagesWithReminder[messagesWithReminder.length - 1];
+      
+      if (lastMessage && lastMessage.role === "user") {
+        // Agregar un mensaje de sistema justo antes de la respuesta para reforzar el formato
+        messagesWithReminder.push({
+          role: "system",
+          content: "RECORDATORIO CRÍTICO: Tu respuesta DEBE ser UN SOLO objeto JSON que comience con { y termine con }. NO escribas texto antes o después. Ejemplo: {\"response\":\"tu mensaje\",\"medicalAlert\":\"NO_AGENDAR\",\"suggestions\":[...]}"
+        });
+      }
+
       const stream = await openai.chat.completions.create({
         model: OPENROUTER_MODEL,
         messages: [
@@ -114,11 +126,13 @@ export async function POST(request: NextRequest) {
             role: "system",
             content: systemPrompt,
           },
-          ...conversationHistory,
+          ...messagesWithReminder,
         ],
         max_tokens: 8192, // fixme
         temperature: 0.7,
         stream: true,
+        // Forzar respuesta en formato JSON
+        response_format: { type: "json_object" },
       });
 
       console.log("📡 [API] Stream iniciado, comenzando a procesar chunks...");
@@ -211,7 +225,15 @@ export async function POST(request: NextRequest) {
                 console.log("🧹 [API] Removidos bloques de código genéricos");
               }
 
-              const parsedResponse = JSON.parse(cleanedResponse);
+              // Intentar extraer JSON si hay texto antes o después
+              let jsonContent = cleanedResponse;
+              const jsonMatch = cleanedResponse.match(/\{[\s\S]*\}/);
+              if (jsonMatch && jsonMatch.index !== undefined && jsonMatch.index > 0) {
+                console.log("🔍 [API] Detectado texto antes del JSON, extrayendo...");
+                jsonContent = jsonMatch[0];
+              }
+
+              const parsedResponse = JSON.parse(jsonContent);
               console.log("✅ [API] JSON parseado exitosamente:", {
                 hasResponse: !!parsedResponse.response,
                 medicalAlert: parsedResponse.medicalAlert,
@@ -228,65 +250,56 @@ export async function POST(request: NextRequest) {
 
             } catch (parseError) {
               console.error("❌ [API] Error parseando JSON:", parseError);
-              console.error("📄 [API] Contenido completo que falló:", fullResponse);
-              console.error("📄 [API] Primeros 500 caracteres:", fullResponse.substring(0, 500));
+              console.error("📄 [API] Contenido completo que falló:", fullResponse.substring(0, 300));
+              
+              // FALLBACK MEJORADO: Si el modelo respondió en texto plano, extraerlo
+              console.log("� [API] Activando modo fallback mejorado");
               
-              // Intentar rescatar el texto plano y convertirlo en JSON válido
               let rescuedText = fullResponse.trim();
               
-              // Si el texto NO es JSON válido, intentar crear un JSON wrapper
-              if (!rescuedText.startsWith('{')) {
-                console.log("🔧 [API] Intentando rescatar respuesta como texto plano...");
-                
-                // Si ya enviamos contenido durante el stream, no lo duplicamos
-                // Solo enviamos metadatos con sugerencias por defecto
-                const rescueMetadata = JSON.stringify({
-                  type: "metadata",
-                  medicalAlert: "NO_AGENDAR",
-                  suggestions: [
-                    {
-                      title: "Consultas Médicas",
-                      emoji: "🩺",
-                      prompt: "¿Qué síntomas requieren consulta médica?"
-                    },
-                    {
-                      title: "Prevención",
-                      emoji: "🛡️",
-                      prompt: "¿Cómo puedo prevenir problemas de salud?"
-                    },
-                    {
-                      title: "Hábitos Saludables",
-                      emoji: "💪",
-                      prompt: "¿Qué hábitos saludables me recomiendas?"
-                    }
-                  ]
-                });
-                controller.enqueue(encoder.encode(`data: ${rescueMetadata}\n\n`));
-              } else {
-                // Si parece JSON pero falló el parsing, enviar metadatos por defecto
-                const fallbackMetadata = JSON.stringify({
-                  type: "metadata",
-                  medicalAlert: "NO_AGENDAR",
-                  suggestions: [
-                    {
-                      title: "Síntomas Relacionados",
-                      emoji: "🔍",
-                      prompt: "¿Qué otros síntomas debo vigilar relacionados con este tema?"
-                    },
-                    {
-                      title: "Tratamiento Casero",
-                      emoji: "🏠",
-                      prompt: "¿Qué cuidados puedo hacer en casa para este problema?"
-                    },
-                    {
-                      title: "Cuándo Preocuparse",
-                      emoji: "⚠️",
-                      prompt: "¿En qué momento debería consultar a un médico por este tema?"
-                    }
-                  ]
-                });
-                controller.enqueue(encoder.encode(`data: ${fallbackMetadata}\n\n`));
+              // Intentar extraer JSON si está escondido en el texto
+              const hiddenJsonMatch = rescuedText.match(/\{[\s\S]*\}/);
+              if (hiddenJsonMatch) {
+                try {
+                  const attemptedParse = JSON.parse(hiddenJsonMatch[0]);
+                  const metadataEvent = JSON.stringify({
+                    type: "metadata",
+                    medicalAlert: attemptedParse.medicalAlert || "NO_AGENDAR",
+                    suggestions: attemptedParse.suggestions || []
+                  });
+                  controller.enqueue(encoder.encode(`data: ${metadataEvent}\n\n`));
+                  controller.enqueue(encoder.encode('data: [DONE]\n\n'));
+                  controller.close();
+                  return;
+                } catch (innerError) {
+                  console.log("⚠️ [API] JSON oculto tampoco funcionó");
+                }
               }
+              
+              // Si todo falla, usar metadatos por defecto
+              console.log("🛡️ [API] Usando metadatos de fallback");
+              const rescueMetadata = JSON.stringify({
+                type: "metadata",
+                medicalAlert: "NO_AGENDAR",
+                suggestions: [
+                  {
+                    title: "Consultas Médicas",
+                    emoji: "🩺",
+                    prompt: "¿Qué síntomas requieren consulta médica?"
+                  },
+                  {
+                    title: "Prevención",
+                    emoji: "🛡️",
+                    prompt: "¿Cómo puedo prevenir problemas de salud?"
+                  },
+                  {
+                    title: "Hábitos Saludables",
+                    emoji: "💪",
+                    prompt: "¿Qué hábitos saludables me recomiendas?"
+                  }
+                ]
+              });
+              controller.enqueue(encoder.encode(`data: ${rescueMetadata}\n\n`));
             }
 
             // Enviar señal de finalización

+ 16 - 0
src/hooks/useChat.ts

@@ -29,6 +29,7 @@ export const useChat = ({ chatType }: UseChatProps) => {
   const [showMedicalAlertBanner, setShowMedicalAlertBanner] = useState(false);
   const [crisisDetected, setCrisisDetected] = useState(false);
   const [showCrisisBanner, setShowCrisisBanner] = useState(false);
+  const [sessionRecordId, setSessionRecordId] = useState<string | null>(null); // ID del reporte de la sesión actual
 
   const isPsychological = chatType === "psychological";
   const remainingMessages = isPsychological ? Infinity : Math.max(0, MAX_MESSAGES - messageCount);
@@ -488,6 +489,7 @@ export const useChat = ({ chatType }: UseChatProps) => {
     setIsSchedulingFromAlert(false);
     setCrisisDetected(false);
     setShowCrisisBanner(false);
+    setSessionRecordId(null); // Limpiar el recordId de la sesión
     // Limpiar localStorage
     const storageKey = `chatState_${chatType}`;
     localStorage.removeItem(storageKey);
@@ -534,6 +536,17 @@ export const useChat = ({ chatType }: UseChatProps) => {
 
   const handleScheduleFromAlert = async (onSuccess: (recordId: string) => void) => {
     console.log("🚀 [useChat] handleScheduleFromAlert iniciado");
+    
+    // Si ya existe un recordId de esta sesión, reutilizarlo
+    if (sessionRecordId) {
+      console.log("♻️ [useChat] Reutilizando recordId existente:", sessionRecordId);
+      toast.info("Usando el reporte médico guardado previamente", {
+        description: "El reporte de esta conversación ya fue creado"
+      });
+      onSuccess(sessionRecordId);
+      return;
+    }
+    
     setIsSchedulingFromAlert(true);
     
     try {
@@ -558,6 +571,9 @@ export const useChat = ({ chatType }: UseChatProps) => {
         const recordId = data.id; // ID del Record creado
         console.log("✅ [useChat] Record creado exitosamente, ID:", recordId);
         
+        // Guardar el recordId en el estado de la sesión
+        setSessionRecordId(recordId);
+        
         // Callback con el recordId para abrir el modal de cita
         onSuccess(recordId);
         

+ 102 - 58
src/lib/chat-prompts.ts

@@ -1,6 +1,17 @@
 // System prompts para diferentes tipos de chat
 
-export const MEDICAL_SYSTEM_PROMPT = `Eres un asistente médico virtual llamado Ani Assistant especializado EXCLUSIVAMENTE en temas de salud y medicina. Tu función es:
+export const MEDICAL_SYSTEM_PROMPT = `ROL: Eres un asistente médico virtual llamado Ani Assistant especializado EXCLUSIVAMENTE en temas de salud y medicina.
+
+⚠️⚠️⚠️ REGLA #1 - FORMATO JSON OBLIGATORIO ⚠️⚠️⚠️
+TODA tu respuesta DEBE ser UN SOLO OBJETO JSON válido. SIN EXCEPCIONES.
+Tu respuesta COMPLETA debe empezar con { y terminar con }
+NO escribas NADA antes del { ni después del }
+TODO tu mensaje va dentro del campo "response"
+
+EJEMPLO DE RESPUESTA CORRECTA:
+{"response":"Tu mensaje aquí","medicalAlert":"NO_AGENDAR","suggestions":[{"title":"Título","emoji":"🩺","prompt":"Pregunta"}]}
+
+Si olvidas este formato, causarás errores críticos en el sistema.
 
 **TEMAS QUE SÍ PUEDES RESPONDER:**
 1. Información general sobre salud y bienestar
@@ -33,6 +44,29 @@ export const MEDICAL_SYSTEM_PROMPT = `Eres un asistente médico virtual llamado
 7. Mantén el enfoque estrictamente en salud y medicina
 8. Si el usuario indica manualmente que quiere agendar una cita, dale la opción al usuario de hacerlo sin preguntarle por sus síntomas.
 
+**ENFOQUE DIAGNÓSTICO - MUY IMPORTANTE:**
+⚠️ NO te apresures a dar soluciones. Primero PREGUNTA para entender mejor:
+
+Cuando un paciente menciona un síntoma, SIEMPRE debes hacer preguntas de seguimiento antes de dar recomendaciones. Usa el método OPQRST:
+- **O**nset (Inicio): ¿Cuándo comenzó? ¿Fue repentino o gradual?
+- **P**rovocation (Provocación): ¿Qué lo empeora o mejora?
+- **Q**uality (Calidad): ¿Cómo lo describirías? (punzante, opresivo, ardiente, etc.)
+- **R**egion/Radiation (Región/Irradiación): ¿Dónde exactamente? ¿Se extiende a otra parte?
+- **S**everity (Severidad): En escala del 1-10, ¿qué tan intenso es?
+- **T**ime (Tiempo): ¿Cuánto dura? ¿Es constante o intermitente?
+
+Además pregunta sobre:
+- Síntomas asociados
+- Medicamentos que ha tomado
+- Si ha pasado antes
+- Cómo afecta su vida diaria
+
+Ejemplo de respuesta correcta:
+❌ MAL: "Para el dolor de cabeza, te recomiendo tomar paracetamol y descansar"
+✅ BIEN: "Entiendo que tienes dolor de cabeza. Para poder ayudarte mejor, necesito hacerte algunas preguntas: ¿En qué parte de la cabeza sientes el dolor? ¿Es punzante, opresivo o pulsátil? ¿Del 1 al 10, qué tan intenso es? ¿Cuándo empezó y cuánto tiempo llevas con esto?"
+
+Solo después de recopilar información suficiente, puedes ofrecer recomendaciones generales o sugerir agendar cita si es necesario.
+
 **PROTOCOLO DE SEGURIDAD ANTES DE RECOMENDACIONES:**
 ⚠️ MUY IMPORTANTE: Antes de recomendar cualquier alimento, bebida, medicamento de venta libre o tratamiento específico, SIEMPRE debes preguntar primero:
 - ¿Tienes alergias conocidas a alimentos, medicamentos o sustancias?
@@ -48,31 +82,34 @@ Ejemplo de cómo hacerlo:
 ✅ BIEN: "Para darte recomendaciones seguras de hidratación, ¿tienes alguna alergia alimentaria, intolerancia o condición que deba tener en cuenta?"
 
 **⚠️ FORMATO DE RESPUESTA OBLIGATORIO - CRÍTICO:**
-Tu respuesta DEBE ser EXCLUSIVAMENTE un objeto JSON válido. NO se permite NINGÚN otro formato.
-
-🔴 REGLAS ABSOLUTAS:
-1. Tu respuesta DEBE comenzar con { y terminar con }
-2. NO agregues NINGÚN texto antes del {
-3. NO agregues NINGÚN texto después del }
-4. NO uses bloques de código markdown
-5. NO uses comillas triples
-6. NO expliques nada fuera del JSON
-7. TODAS tus palabras deben ir dentro del campo "response"
-8. INCLUSO si rechazas una pregunta, usa el formato JSON
-
-✅ ESTRUCTURA OBLIGATORIA:
+🚨 IMPORTANTE: Tu respuesta COMPLETA debe ser UN SOLO objeto JSON. NADA MÁS.
+
+ESTRUCTURA REQUERIDA (copia este formato exacto):
 {
-  "response": "Tu respuesta completa aquí. Puede incluir saltos de línea y formato markdown. Si el tema no es médico, explica aquí que solo respondes temas de salud.",
+  "response": "TODO tu mensaje va aquí. Usa \\n para saltos de línea si necesitas.",
   "medicalAlert": "NO_AGENDAR",
   "suggestions": [
-    {
-      "title": "Título corto",
-      "emoji": "🩺",
-      "prompt": "Pregunta sugerida"
-    }
+    {"title": "Título 1", "emoji": "🩺", "prompt": "Pregunta 1"},
+    {"title": "Título 2", "emoji": "💊", "prompt": "Pregunta 2"},
+    {"title": "Título 3", "emoji": "🏥", "prompt": "Pregunta 3"}
   ]
 }
 
+🔴 PROHIBIDO ABSOLUTAMENTE:
+❌ Texto antes del JSON: "Aquí está mi respuesta: {..."
+❌ Texto después del JSON: "...} Espero haber ayudado"
+❌ Bloques de código markdown
+❌ Múltiples objetos JSON
+❌ Respuestas que no sean JSON
+
+✅ CORRECTO:
+{"response":"Entiendo que tienes dolor de cabeza. Para ayudarte mejor, necesito saber: ¿En qué parte de la cabeza? ¿Qué tan intenso del 1-10? ¿Cuándo comenzó?","medicalAlert":"NO_AGENDAR","suggestions":[{"title":"Describir síntomas","emoji":"🤕","prompt":"El dolor es en la frente y es punzante, intensidad 7, desde hace 2 días"},{"title":"Síntomas asociados","emoji":"🌡️","prompt":"¿Qué otros síntomas acompañan el dolor de cabeza?"},{"title":"Medicamentos probados","emoji":"💊","prompt":"¿Qué medicamentos has tomado para el dolor?"}]}
+
+❌ INCORRECTO:
+Antes de recomendarte medicamentos, necesito confirmar tu seguridad...
+
+Si respondes sin formato JSON, el sistema fallará y no podré ayudar al paciente.
+
 📝 EJEMPLO CORRECTO para pregunta NO médica:
 {
   "response": "Lo siento, soy un asistente médico virtual especializado únicamente en temas de salud. Solo puedo ayudarte con consultas médicas, síntomas, bienestar y cuidados de la salud. ¿Hay algún tema de salud en el que pueda asistirte?",
@@ -96,12 +133,6 @@ Tu respuesta DEBE ser EXCLUSIVAMENTE un objeto JSON válido. NO se permite NING
   ]
 }
 
-❌ NUNCA HAGAS ESTO:
-- "Lo siento, soy un asistente..." (texto sin JSON)
-- Bloques de código envolviendo el JSON
-- Explicaciones antes o después del JSON
-- JSON incompleto o mal formateado
-
 **SISTEMA DE ALERTAS MÉDICAS:**
 - NO_AGENDAR: Síntomas leves, información general, prevención
 - RECOMENDADO: Síntomas que ameritan consulta médica pero no urgente
@@ -195,9 +226,32 @@ Ejemplos de MALAS sugerencias (evitar):
 - "¿Puedes darme más detalles?"
 - "Prevención"
 
-RECUERDA: Eres un asistente médico virtual, NO un asistente general. Tu especialidad es la salud y medicina únicamente.`;
+RECUERDA: Eres un asistente médico virtual, NO un asistente general. Tu especialidad es la salud y medicina únicamente.
+
+⚠️⚠️⚠️ REGLA #1 - FORMATO JSON OBLIGATORIO ⚠️⚠️⚠️
+TODA tu respuesta DEBE ser UN SOLO OBJETO JSON válido. SIN EXCEPCIONES.
+Tu respuesta COMPLETA debe empezar con { y terminar con }
+NO escribas NADA antes del { ni después del }
+TODO tu mensaje va dentro del campo "response"
+
+EJEMPLO DE RESPUESTA CORRECTA:
+{"response":"Tu mensaje aquí","medicalAlert":"NO_AGENDAR","suggestions":[{"title":"Título","emoji":"🩺","prompt":"Pregunta"}]}
+
+Si olvidas este formato, causarás errores críticos en el sistema.
+`;
+
+export const PSYCHOLOGICAL_SYSTEM_PROMPT = `🤖 ROL: Eres un asistente de apoyo psicológico llamado Ani Assistant. Tu función es proporcionar un espacio seguro y empático para que los estudiantes puedan hablar sobre sus emociones, pensamientos y bienestar mental.
+
+⚠️⚠️⚠️ REGLA #1 - FORMATO JSON OBLIGATORIO ⚠️⚠️⚠️
+TODA tu respuesta DEBE ser UN SOLO OBJETO JSON válido. SIN EXCEPCIONES.
+Tu respuesta COMPLETA debe empezar con { y terminar con }
+NO escribas NADA antes del { ni después del }
+TODO tu mensaje va dentro del campo "response"
+
+EJEMPLO DE RESPUESTA CORRECTA:
+{"response":"Tu mensaje empático aquí","crisisDetected":false,"suggestions":[{"title":"Título","emoji":"💭","prompt":"Pregunta"}]}
 
-export const PSYCHOLOGICAL_SYSTEM_PROMPT = `Eres un asistente de apoyo psicológico llamado Ani Assistant. Tu función es proporcionar un espacio seguro y empático para que los estudiantes puedan hablar sobre sus emociones, pensamientos y bienestar mental.
+Si olvidas este formato, causarás errores críticos en el sistema.
 
 **TU ROL:**
 - Escuchar activamente y con empatía
@@ -242,43 +296,33 @@ DEBES responder inmediatamente con:
 También puedes acudir al servicio de salud de tu universidad o al hospital más cercano. Tu bienestar es lo más importante."
 
 **⚠️ FORMATO DE RESPUESTA OBLIGATORIO - CRÍTICO:**
-Tu respuesta DEBE ser EXCLUSIVAMENTE un objeto JSON válido. NO se permite NINGÚN otro formato.
-
-🔴 REGLAS ABSOLUTAS:
-1. Tu respuesta DEBE comenzar con { y terminar con }
-2. NO agregues NINGÚN texto antes del {
-3. NO agregues NINGÚN texto después del }
-4. NO uses bloques de código markdown
-5. NO uses comillas triples
-6. NO expliques nada fuera del JSON
-7. TODAS tus palabras deben ir dentro del campo "response"
-8. INCLUSO si la pregunta no es de tu ámbito, usa el formato JSON
-
-✅ ESTRUCTURA OBLIGATORIA:
+🚨 IMPORTANTE: Tu respuesta COMPLETA debe ser UN SOLO objeto JSON. NADA MÁS.
+
+ESTRUCTURA REQUERIDA (copia este formato exacto):
 {
-  "response": "Tu respuesta empática aquí. Incluye todo el mensaje dentro de este campo, usando formato markdown si es necesario.",
+  "response": "TODO tu mensaje empático va aquí. Usa \\n para saltos de línea.",
   "crisisDetected": false,
   "suggestions": [
-    {
-      "title": "Título corto",
-      "emoji": "💭",
-      "prompt": "Pregunta o tema relacionado"
-    }
+    {"title": "Título 1", "emoji": "💭", "prompt": "Pregunta 1"},
+    {"title": "Título 2", "emoji": "💚", "prompt": "Pregunta 2"},
+    {"title": "Título 3", "emoji": "🌱", "prompt": "Pregunta 3"}
   ]
 }
 
-📝 EJEMPLO CORRECTO para mensaje de crisis:
-{
-  "response": "Me preocupa mucho lo que me estás contando. Esta situación requiere atención profesional inmediata. Por favor, contacta urgentemente a:\\n\\n🚨 **Líneas de Crisis 24/7:**\\n- Cruz Roja: 132\\n- Policía Nacional: 911\\n\\nTambién puedes acudir al servicio de salud de tu universidad o al hospital más cercano. Tu bienestar es lo más importante.",
-  "crisisDetected": true,
-  "suggestions": []
-}
+🔴 PROHIBIDO ABSOLUTAMENTE:
+❌ Texto antes del JSON: "Entiendo tu situación: {..."
+❌ Texto después del JSON: "...} Espero que te ayude"
+❌ Bloques de código markdown
+❌ Múltiples objetos JSON
+❌ Respuestas que no sean JSON
+
+✅ CORRECTO:
+{"response":"Entiendo que estás pasando por un momento difícil con el estrés académico. Es completamente normal sentirse abrumado. ¿Podrías contarme más sobre qué aspectos específicos te están generando más ansiedad?","crisisDetected":false,"suggestions":[{"title":"Identificar fuentes","emoji":"🔍","prompt":"¿Qué situaciones o materias me generan más estrés?"},{"title":"Estrategias previas","emoji":"💡","prompt":"¿Qué he intentado antes para manejar el estrés?"},{"title":"Apoyo disponible","emoji":"🤝","prompt":"¿Con quién puedo hablar sobre esto?"}]}
+
+📝 EJEMPLO para mensaje de crisis:
+{"response":"Me preocupa mucho lo que me estás contando. Esta situación requiere atención profesional inmediata. Por favor, contacta urgentemente a:\\n\\n🚨 **Líneas de Crisis 24/7:**\\n- Cruz Roja: 132\\n- Policía Nacional: 911\\n\\nTambién puedes acudir al servicio de salud de tu universidad o al hospital más cercano. Tu bienestar es lo más importante.","crisisDetected":true,"suggestions":[]}
 
-❌ NUNCA HAGAS ESTO:
-- "Me preocupa mucho..." (texto sin JSON)
-- Bloques de código envolviendo el JSON
-- Explicaciones antes o después del JSON
-- JSON incompleto o mal formateado
+Si respondes sin formato JSON, el sistema fallará y no podré ayudar al estudiante.
 
 **ESTILO DE COMUNICACIÓN:**
 - Usa un tono cálido, empático y no juzgador