Bläddra i källkod

wdym i can save the record twice lmao

Matthew Trejo 1 månad sedan
förälder
incheckning
95d7c2a277
5 ändrade filer med 313 tillägg och 115 borttagningar
  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-switch": "^1.2.6",
         "@radix-ui/react-tabs": "^1.1.13",
         "@radix-ui/react-tabs": "^1.1.13",
         "@radix-ui/react-toast": "^1.2.14",
         "@radix-ui/react-toast": "^1.2.14",
+        "@radix-ui/react-tooltip": "^1.2.8",
         "@react-pdf/renderer": "^4.3.0",
         "@react-pdf/renderer": "^4.3.0",
         "@types/bcryptjs": "^2.4.6",
         "@types/bcryptjs": "^2.4.6",
         "ai": "^4.3.19",
         "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": {
     "node_modules/@radix-ui/react-use-callback-ref": {
       "version": "1.1.1",
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz",
       "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-switch": "^1.2.6",
     "@radix-ui/react-tabs": "^1.1.13",
     "@radix-ui/react-tabs": "^1.1.13",
     "@radix-ui/react-toast": "^1.2.14",
     "@radix-ui/react-toast": "^1.2.14",
+    "@radix-ui/react-tooltip": "^1.2.8",
     "@react-pdf/renderer": "^4.3.0",
     "@react-pdf/renderer": "^4.3.0",
     "@types/bcryptjs": "^2.4.6",
     "@types/bcryptjs": "^2.4.6",
     "ai": "^4.3.19",
     "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");
       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({
       const stream = await openai.chat.completions.create({
         model: OPENROUTER_MODEL,
         model: OPENROUTER_MODEL,
         messages: [
         messages: [
@@ -114,11 +126,13 @@ export async function POST(request: NextRequest) {
             role: "system",
             role: "system",
             content: systemPrompt,
             content: systemPrompt,
           },
           },
-          ...conversationHistory,
+          ...messagesWithReminder,
         ],
         ],
         max_tokens: 8192, // fixme
         max_tokens: 8192, // fixme
         temperature: 0.7,
         temperature: 0.7,
         stream: true,
         stream: true,
+        // Forzar respuesta en formato JSON
+        response_format: { type: "json_object" },
       });
       });
 
 
       console.log("📡 [API] Stream iniciado, comenzando a procesar chunks...");
       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");
                 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:", {
               console.log("✅ [API] JSON parseado exitosamente:", {
                 hasResponse: !!parsedResponse.response,
                 hasResponse: !!parsedResponse.response,
                 medicalAlert: parsedResponse.medicalAlert,
                 medicalAlert: parsedResponse.medicalAlert,
@@ -228,65 +250,56 @@ export async function POST(request: NextRequest) {
 
 
             } catch (parseError) {
             } catch (parseError) {
               console.error("❌ [API] Error parseando JSON:", 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();
               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
             // 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 [showMedicalAlertBanner, setShowMedicalAlertBanner] = useState(false);
   const [crisisDetected, setCrisisDetected] = useState(false);
   const [crisisDetected, setCrisisDetected] = useState(false);
   const [showCrisisBanner, setShowCrisisBanner] = 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 isPsychological = chatType === "psychological";
   const remainingMessages = isPsychological ? Infinity : Math.max(0, MAX_MESSAGES - messageCount);
   const remainingMessages = isPsychological ? Infinity : Math.max(0, MAX_MESSAGES - messageCount);
@@ -488,6 +489,7 @@ export const useChat = ({ chatType }: UseChatProps) => {
     setIsSchedulingFromAlert(false);
     setIsSchedulingFromAlert(false);
     setCrisisDetected(false);
     setCrisisDetected(false);
     setShowCrisisBanner(false);
     setShowCrisisBanner(false);
+    setSessionRecordId(null); // Limpiar el recordId de la sesión
     // Limpiar localStorage
     // Limpiar localStorage
     const storageKey = `chatState_${chatType}`;
     const storageKey = `chatState_${chatType}`;
     localStorage.removeItem(storageKey);
     localStorage.removeItem(storageKey);
@@ -534,6 +536,17 @@ export const useChat = ({ chatType }: UseChatProps) => {
 
 
   const handleScheduleFromAlert = async (onSuccess: (recordId: string) => void) => {
   const handleScheduleFromAlert = async (onSuccess: (recordId: string) => void) => {
     console.log("🚀 [useChat] handleScheduleFromAlert iniciado");
     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);
     setIsSchedulingFromAlert(true);
     
     
     try {
     try {
@@ -558,6 +571,9 @@ export const useChat = ({ chatType }: UseChatProps) => {
         const recordId = data.id; // ID del Record creado
         const recordId = data.id; // ID del Record creado
         console.log("✅ [useChat] Record creado exitosamente, ID:", recordId);
         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
         // Callback con el recordId para abrir el modal de cita
         onSuccess(recordId);
         onSuccess(recordId);
         
         

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

@@ -1,6 +1,17 @@
 // System prompts para diferentes tipos de chat
 // 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:**
 **TEMAS QUE SÍ PUEDES RESPONDER:**
 1. Información general sobre salud y bienestar
 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
 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.
 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:**
 **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:
 ⚠️ 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?
 - ¿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?"
 ✅ 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:**
 **⚠️ 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",
   "medicalAlert": "NO_AGENDAR",
   "suggestions": [
   "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:
 📝 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?",
   "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:**
 **SISTEMA DE ALERTAS MÉDICAS:**
 - NO_AGENDAR: Síntomas leves, información general, prevención
 - NO_AGENDAR: Síntomas leves, información general, prevención
 - RECOMENDADO: Síntomas que ameritan consulta médica pero no urgente
 - 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?"
 - "¿Puedes darme más detalles?"
 - "Prevención"
 - "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:**
 **TU ROL:**
 - Escuchar activamente y con empatía
 - 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."
 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:**
 **⚠️ 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,
   "crisisDetected": false,
   "suggestions": [
   "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:**
 **ESTILO DE COMUNICACIÓN:**
 - Usa un tono cálido, empático y no juzgador
 - Usa un tono cálido, empático y no juzgador