{"id":178,"date":"2025-03-25T05:51:35","date_gmt":"2025-03-25T05:51:35","guid":{"rendered":"https:\/\/neocloud05.com\/neo\/?page_id=178"},"modified":"2025-03-25T07:16:40","modified_gmt":"2025-03-25T07:16:40","slug":"laboratorio-modelo-conversacional-perfiles","status":"publish","type":"page","link":"https:\/\/neocloud05.com\/neo\/laboratorio-modelo-conversacional-perfiles\/","title":{"rendered":"Laboratorio 2.0.3 Modelo Conversacional con Perfiles"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"178\" class=\"elementor elementor-178\">\n\t\t\t\t<div class=\"elementor-element elementor-element-dead8d3 e-flex e-con-boxed e-con e-parent\" data-id=\"dead8d3\" data-element_type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-41ce17a elementor-widget elementor-widget-heading\" data-id=\"41ce17a\" data-element_type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">Neo 2.0.3 Beta<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-9f96e12 e-flex e-con-boxed e-con e-parent\" data-id=\"9f96e12\" data-element_type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-ef60ff3 elementor-widget elementor-widget-html\" data-id=\"ef60ff3\" data-element_type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<!--\nconst OPENAI_API_KEY = 'Bearer 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'; \/\/ Reemplaza con tu API Key\n-->\n\n<!DOCTYPE html>\n<html lang=\"es\">\n<head>\n  <meta charset=\"UTF-8\" \/>\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"\/>\n  <title>Asistente AI con Voz Continua<\/title>\n  <style>\n    body {\n      background: #f0f2f5;\n      font-family: 'Segoe UI', sans-serif;\n      margin: 0;\n      padding: 20px;\n      display: flex;\n      flex-direction: column;\n      align-items: center;\n    }\n\n    .chat-container {\n      background: #fff;\n      width: 100%;\n      max-width: 600px;\n      height: 80vh;\n      border-radius: 12px;\n      box-shadow: 0 4px 12px rgba(0,0,0,0.1);\n      display: flex;\n      flex-direction: column;\n      overflow: hidden;\n    }\n\n    .chat-header {\n      color: white;\n      padding: 16px;\n      font-size: 18px;\n      font-weight: bold;\n      transition: background-color 0.4s ease;\n    }\n    \n    .bg-general { background-color: #4f46e5; }         \/* Morado *\/\n    .bg-educador { background-color: #2563eb; }        \/* Azul *\/\n    .bg-coach { background-color: #f59e0b; }           \/* Naranja *\/\n    .bg-psicologo { background-color: #6366f1; }       \/* \u00cdndigo *\/\n    .bg-ejecutivo { background-color: #111827; }       \/* Gris oscuro *\/\n    .bg-cliente { background-color: #10b981; }         \/* Verde *\/\n    .bg-soporte { background-color: #6b7280; }         \/* Gris medio *\/\n    .bg-entrenador { background-color: #ef4444; }      \/* Rojo *\/\n    .bg-neuro { background-color: #7c3aed; }           \/* Violeta *\/\n    .bg-nutricionista { background-color: #22c55e; }   \/* Verde lima *\/\n\n    .chat-body {\n      flex: 1;\n      padding: 16px;\n      overflow-y: auto;\n      background: #f9fafb;\n    }\n\n    .message {\n      margin-bottom: 12px;\n    }\n\n    .user {\n      text-align: right;\n    }\n\n    .bot {\n      text-align: left;\n    }\n\n    .message p {\n      display: inline-block;\n      padding: 10px 14px;\n      border-radius: 20px;\n      max-width: 80%;\n    }\n\n    .user p {\n      background: #4f46e5;\n      color: white;\n    }\n\n    .bot p {\n      background: #e5e7eb;\n      color: #111827;\n    }\n\n    .chat-input {\n      display: flex;\n      padding: 10px;\n      border-top: 1px solid #e5e7eb;\n      background: #fff;\n    }\n\n    .chat-input input {\n      flex: 1;\n      padding: 10px;\n      border: 1px solid #d1d5db;\n      border-radius: 8px;\n      margin-right: 10px;\n      font-size: 16px;\n    }\n\n    .chat-input button {\n      padding: 10px;\n      background: #4f46e5;\n      color: white;\n      border: none;\n      border-radius: 8px;\n      cursor: pointer;\n      font-weight: bold;\n      margin-left: 5px;\n    }\n\n    .chat-input button:hover {\n      background: #4338ca;\n    }\n\n    .voice-controls {\n      margin-top: 15px;\n      display: flex;\n      gap: 10px;\n      flex-wrap: wrap;\n    }\n\n    .test-voice-button, .continuous-button {\n      padding: 10px 20px;\n      font-weight: bold;\n      background: #10b981;\n      color: white;\n      border: none;\n      border-radius: 8px;\n      cursor: pointer;\n    }\n\n    .test-voice-button:hover, .continuous-button:hover {\n      background: #059669;\n    }\n\n    #voiceSelect {\n      padding: 8px;\n      border-radius: 6px;\n      font-size: 14px;\n      margin-top: 10px;\n    }\n\n    .voice-status {\n      margin-top: 8px;\n      font-size: 14px;\n      color: green;\n      font-weight: bold;\n      display: none;\n    }\n    \n    .timestamp {\n      display: block;\n      font-size: 11px;\n      color: #6b7280;\n      margin-top: 4px;\n      padding-left: 6px;\n    }\n    \n    .message {\n      display: flex;\n      align-items: flex-start;\n      gap: 10px;\n    }\n    \n    .user {\n      flex-direction: row-reverse;\n      text-align: right;\n    }\n    \n    .message-avatar {\n      width: 40px;\n      height: 40px;\n      border-radius: 50%;\n      background-color: #d1d5db;\n      display: flex;\n      align-items: center;\n      justify-content: center;\n      font-size: 20px;\n      flex-shrink: 0;\n    }\n    \n    .user .message-avatar {\n      background-color: #4f46e5;\n      color: white;\n    }\n    \n    .bot .message-avatar {\n      background-color: #e5e7eb;\n      color: #111827;\n    }\n    \n    .message p {\n      margin: 0;\n    }\n    \n    #voz-status {\n      animation: pulse 1.5s infinite;\n    }\n    \n    @keyframes pulse {\n      0% { opacity: 1; }\n      50% { opacity: 0.4; }\n      100% { opacity: 1; }\n    }\n\n  <\/style>\n<\/head>\n<body>\n<div style=\"margin-top: 15px;\">\n  <label for=\"profileSelect\"><strong>Selecciona un perfil profesional del asistente:<\/strong><\/label>\n  <select id=\"profileSelect\">\n    <option value=\"asistente_general\">\ud83e\udd16Asistente General<\/option>\n    <option value=\"educador\">\ud83d\udc68\u200d\ud83c\udfeb Educador<\/option>\n    <option value=\"coach\">\ud83c\udfaf Coach<\/option>\n    <option value=\"psicologo\">\ud83e\udde0 Psic\u00f3logo<\/option>\n    <option value=\"ejecutivo_cuenta\">\ud83d\udcbc Ejecutivo de Cuenta<\/option>\n    <option value=\"servicio_cliente\">\ud83d\udcde Servicio al Cliente<\/option>\n    <option value=\"soporte_tecnico\">\ud83d\udee0\ufe0f Soporte T\u00e9cnico<\/option>\n    <option value=\"entrenador_personal\">\ud83c\udfcb\ufe0f Entrenador Personal<\/option>\n    <option value=\"neurocientifico\">\ud83e\uddec Neurocient\u00edfico<\/option>\n    <option value=\"nutricionista\">\ud83e\udd57 Nutricionista<\/option>\n  <\/select>\n<\/div>\n\n\n  <div class=\"chat-container\">\n    <div class=\"chat-header\" id=\"chat-title\">\ud83e\udd16 Asistente IA con Voz<\/div>\n      <div id=\"voz-status\" style=\"display: none; text-align: center; padding: 8px; font-size: 14px; color: #10b981; font-weight: bold;\">\n        \ud83d\udd0a El asistente est\u00e1 hablando...\n    <\/div>\n    <div class=\"chat-body\" id=\"chat-body\"><\/div>\n    <div class=\"chat-input\">\n      <input type=\"text\" id=\"user-input\" placeholder=\"Escribe o habla tu mensaje...\" \/>\n      <button onclick=\"sendMessage()\">Enviar<\/button>\n      <button id=\"mic-button\" title=\"Hablar por micr\u00f3fono\">\ud83c\udf99\ufe0f<\/button>\n    <\/div>\n  <\/div>\n\n  <div class=\"voice-controls\">\n    <button class=\"test-voice-button\" onclick=\"probarVoz()\">\ud83d\udd0a Probar Voz<\/button>\n    <button class=\"continuous-button\" id=\"toggle-continuous\">\ud83c\udfa4 Activar Conversaci\u00f3n<\/button>\n  <\/div>\n\n  <div>\n    <label for=\"voiceSelect\"><strong>Selecciona una voz:<\/strong><\/label>\n    <select id=\"voiceSelect\"><\/select>\n    <div id=\"voiceSavedMsg\" class=\"voice-status\">\u2705 Voz guardada correctamente<\/div>\n  <\/div>\n<div style=\"margin-top: 10px;\">\n  <button onclick=\"borrarHistorial()\" style=\"padding: 8px 16px; background: #ef4444; color: white; border: none; border-radius: 6px; cursor: pointer;\">\ud83d\uddd1\ufe0f Borrar historial<\/button>\n<\/div>\n  <script>\n    const chatBody = document.getElementById('chat-body');\n    const userInput = document.getElementById('user-input');\n    const micButton = document.getElementById('mic-button');\n    const toggleContinuous = document.getElementById('toggle-continuous');\n    const voiceSelect = document.getElementById('voiceSelect');\n    const voiceSavedMsg = document.getElementById('voiceSavedMsg');\n\n    let chatHistory = [];\n    let voces = [];\n    let continuousMode = false;\n    let speaking = false;\n\n    const recognition = window.SpeechRecognition ? new SpeechRecognition() : new webkitSpeechRecognition();\n    recognition.lang = 'es-ES';\n    recognition.interimResults = false;\n    recognition.continuous = false;\n\n    speechSynthesis.onvoiceschanged = () => {\n      voces = speechSynthesis.getVoices().filter(v => v.lang.includes('es'));\n\n      const addedNames = new Set();\n      voiceSelect.innerHTML = '';\n\n      voces.forEach((voz, index) => {\n        if (!addedNames.has(voz.name)) {\n          const option = document.createElement('option');\n          option.value = index;\n          option.textContent = `${voz.name} (${voz.lang})`;\n          voiceSelect.appendChild(option);\n          addedNames.add(voz.name);\n        }\n      });\n\n      const savedVoiceIndex = localStorage.getItem('selectedVoiceIndex');\n      if (savedVoiceIndex && voces[savedVoiceIndex]) {\n        voiceSelect.value = savedVoiceIndex;\n      }\n    };\n\n    voiceSelect.addEventListener('change', () => {\n      localStorage.setItem('selectedVoiceIndex', voiceSelect.value);\n      voiceSavedMsg.style.display = 'block';\n      setTimeout(() => voiceSavedMsg.style.display = 'none', 2000);\n    });\n    \n    function actualizarEncabezadoPorPerfil(perfil) {\n      const titulo = document.getElementById('chat-title');\n      const clases = titulo.classList;\n    \n      \/\/ Elimina cualquier clase de fondo anterior\n      clases.remove(\n        'bg-general', 'bg-educador', 'bg-coach', 'bg-psicologo',\n        'bg-ejecutivo', 'bg-cliente', 'bg-soporte',\n        'bg-entrenador', 'bg-neuro', 'bg-nutricionista'\n      );\n    \n      switch (perfil) {\n        case 'educador':\n          titulo.textContent = '\ud83d\udc68\u200d\ud83c\udfeb Educador IA';\n          clases.add('bg-educador');\n          break;\n        case 'coach':\n          titulo.textContent = '\ud83c\udfaf Coach Motivacional';\n          clases.add('bg-coach');\n          break;\n        case 'psicologo':\n          titulo.textContent = '\ud83e\udde0 Psic\u00f3logo Virtual';\n          clases.add('bg-psicologo');\n          break;\n        case 'ejecutivo_cuenta':\n          titulo.textContent = '\ud83d\udcbc Ejecutivo de Cuenta';\n          clases.add('bg-ejecutivo');\n          break;\n        case 'servicio_cliente':\n          titulo.textContent = '\ud83d\udcde Asistente de Atenci\u00f3n al Cliente';\n          clases.add('bg-cliente');\n          break;\n        case 'soporte_tecnico':\n          titulo.textContent = '\ud83d\udee0\ufe0f Soporte T\u00e9cnico';\n          clases.add('bg-soporte');\n          break;\n        case 'entrenador_personal':\n          titulo.textContent = '\ud83c\udfcb\ufe0f Entrenador Personal';\n          clases.add('bg-entrenador');\n          break;\n        case 'neurocientifico':\n          titulo.textContent = '\ud83e\uddec Neurocient\u00edfico IA';\n          clases.add('bg-neuro');\n          break;\n        case 'nutricionista':\n          titulo.textContent = '\ud83e\udd57 Nutricionista Virtual';\n          clases.add('bg-nutricionista');\n          break;\n        default:\n          titulo.textContent = '\ud83e\udd16 Asistente IA General';\n          clases.add('bg-general');\n      }\n    }\n    \n    function obtenerNombreAsistente(perfil) {\n      switch (perfil) {\n        case 'educador': return 'EduAI';\n        case 'coach': return 'CoachGPT';\n        case 'psicologo': return 'PsiBot';\n        case 'ejecutivo_cuenta': return 'SalesBot';\n        case 'servicio_cliente': return 'AtentoAI';\n        case 'soporte_tecnico': return 'TechAssist';\n        case 'entrenador_personal': return 'FitBot';\n        case 'neurocientifico': return 'NeuroAI';\n        case 'nutricionista': return 'NutriBot';\n        default: return 'Asistente AI';\n      }\n    }\n    \n    function actualizarEncabezadoPorPerfil(perfil) {\n      const titulo = document.getElementById('chat-title');\n      const clases = titulo.classList;\n      const nombre = obtenerNombreAsistente(perfil);\n    \n      clases.remove(\n        'bg-general', 'bg-educador', 'bg-coach', 'bg-psicologo',\n        'bg-ejecutivo', 'bg-cliente', 'bg-soporte',\n        'bg-entrenador', 'bg-neuro', 'bg-nutricionista'\n      );\n    \n      switch (perfil) {\n        case 'educador':\n          titulo.textContent = `\ud83d\udc68\u200d\ud83c\udfeb ${nombre}`;\n          clases.add('bg-educador');\n          break;\n        case 'coach':\n          titulo.textContent = `\ud83c\udfaf ${nombre}`;\n          clases.add('bg-coach');\n          break;\n        case 'psicologo':\n          titulo.textContent = `\ud83e\udde0 ${nombre}`;\n          clases.add('bg-psicologo');\n          break;\n        case 'ejecutivo_cuenta':\n          titulo.textContent = `\ud83d\udcbc ${nombre}`;\n          clases.add('bg-ejecutivo');\n          break;\n        case 'servicio_cliente':\n          titulo.textContent = `\ud83d\udcde ${nombre}`;\n          clases.add('bg-cliente');\n          break;\n        case 'soporte_tecnico':\n          titulo.textContent = `\ud83d\udee0\ufe0f ${nombre}`;\n          clases.add('bg-soporte');\n          break;\n        case 'entrenador_personal':\n          titulo.textContent = `\ud83c\udfcb\ufe0f ${nombre}`;\n          clases.add('bg-entrenador');\n          break;\n        case 'neurocientifico':\n          titulo.textContent = `\ud83e\uddec ${nombre}`;\n          clases.add('bg-neuro');\n          break;\n        case 'nutricionista':\n          titulo.textContent = `\ud83e\udd57 ${nombre}`;\n          clases.add('bg-nutricionista');\n          break;\n        default:\n          titulo.textContent = `\ud83e\udd16 ${nombre}`;\n          clases.add('bg-general');\n      }\n    }\n  \n    function obtenerMensajeSistemaPorPerfil(perfil) {\n      switch (perfil) {\n        case 'educador':\n          return 'Eres un educador paciente y claro. Explica conceptos de forma sencilla, con ejemplos cuando sea posible.';\n        case 'coach':\n          return 'Eres un coach motivacional. Inspiras y ayudas a las personas a alcanzar sus metas con energ\u00eda y empat\u00eda.';\n        case 'psicologo':\n          return 'Eres un psic\u00f3logo emp\u00e1tico y profesional. Responde con sensibilidad, escucha activa y consejos basados en la psicolog\u00eda.';\n        case 'ejecutivo_cuenta':\n          return 'Eres un ejecutivo de cuenta experto en ventas. Responde con enfoque comercial, orientado a resultados y siempre cordial.';\n        case 'servicio_cliente':\n          return 'Eres un agente de servicio al cliente. S\u00e9 amable, resolutivo y busca siempre ayudar al usuario con sus necesidades.';\n        case 'soporte_tecnico':\n          return 'Eres un especialista en soporte t\u00e9cnico. Proporciona soluciones claras y t\u00e9cnicas a problemas relacionados con tecnolog\u00eda.';\n        case 'entrenador_personal':\n          return 'Eres un entrenador personal motivador. Da consejos de ejercicio y bienestar f\u00edsico de forma clara y energ\u00e9tica.';\n        case 'neurocientifico':\n          return 'Eres un neurocient\u00edfico experto. Explica procesos cognitivos y del cerebro con precisi\u00f3n y un lenguaje t\u00e9cnico pero accesible.';\n        case 'nutricionista':\n          return 'Eres un nutricionista profesional. Da consejos sobre alimentaci\u00f3n y salud de forma clara, cient\u00edfica y adaptada a cada persona.';\n        default:\n          return 'Eres un asistente general amable y eficiente. Ayudas con tareas diversas de forma clara, \u00fatil y amigable.';\n      }\n    }\n    \n    function obtenerEmojiPorPerfil(perfil) {\n      switch (perfil) {\n        case 'educador': return '\ud83d\udc68\u200d\ud83c\udfeb';\n        case 'coach': return '\ud83c\udfaf';\n        case 'psicologo': return '\ud83e\udde0';\n        case 'ejecutivo_cuenta': return '\ud83d\udcbc';\n        case 'servicio_cliente': return '\ud83d\udcde';\n        case 'soporte_tecnico': return '\ud83d\udee0\ufe0f';\n        case 'entrenador_personal': return '\ud83c\udfcb\ufe0f';\n        case 'neurocientifico': return '\ud83e\uddec';\n        case 'nutricionista': return '\ud83e\udd57';\n        default: return '\ud83e\udd16';\n      }\n    }\n\n    document.getElementById('profileSelect').addEventListener('change', (e) => {\n      const perfil = e.target.value;\n      localStorage.setItem('perfilAsistente', perfil);\n      actualizarEncabezadoPorPerfil(perfil);\n    });\n    \n    window.addEventListener('DOMContentLoaded', () => {\n      const perfilGuardado = localStorage.getItem('perfilAsistente');\n      const perfilSelect = document.getElementById('profileSelect');\n    \n      if (perfilGuardado) {\n        perfilSelect.value = perfilGuardado;\n        actualizarEncabezadoPorPerfil(perfilGuardado);\n      } else {\n        actualizarEncabezadoPorPerfil(perfilSelect.value); \/\/ Valor por defecto\n      }\n    \n      \/\/ Tambi\u00e9n actualizamos la voz guardada si existe\n      const savedVoiceIndex = localStorage.getItem('selectedVoiceIndex');\n      if (savedVoiceIndex && voces[savedVoiceIndex]) {\n        voiceSelect.value = savedVoiceIndex;\n      }\n      \n      const historialGuardado = localStorage.getItem('historialChat');\n        if (historialGuardado) {\n          try {\n            chatHistory = JSON.parse(historialGuardado);\n            chatHistory.forEach(msg => {\n              const texto = msg.content;\n              const sender = msg.role === 'user' ? 'user' : 'bot';\n            \n              \/\/ Si tiene hora guardada, \u00fasala\n              if (msg.time) {\n                const customDate = new Date(msg.time);\n                const hora = customDate.toLocaleTimeString('es-ES', { hour: '2-digit', minute: '2-digit' });\n                const fecha = customDate.toLocaleDateString('es-ES');\n                appendMessage(sender, `${texto}\\n\\n(${fecha} ${hora})`);\n              } else {\n                appendMessage(sender, texto);\n              }\n            });\n\n            chatBody.scrollTop = chatBody.scrollHeight;\n          } catch (e) {\n            console.error('Error al cargar historial guardado:', e);\n          }\n        }\n\n    });\n\n\n    async function sendMessage(text = null) {\n      const message = text || userInput.value.trim();\n      if (!message) return;\n    \n      appendMessage('user', message);\n      chatHistory.push({ role: 'user', content: message });\n      userInput.value = '';\n      chatBody.scrollTop = chatBody.scrollHeight;\n    \n      appendMessage('bot', '\ud83e\udde0 Pensando...');\n    \n      try {\n        const perfilSeleccionado = document.getElementById('profileSelect').value;\n        const systemPrompt = {\n          role: 'system',\n          content: obtenerMensajeSistemaPorPerfil(perfilSeleccionado)\n        };\n    \n        const response = await fetch('https:\/\/api.openai.com\/v1\/chat\/completions', {\n          method: 'POST',\n          headers: {\n            'Authorization': 'Bearer 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',\n            'Content-Type': 'application\/json',\n            'Content-Type': 'application\/json'\n          },\n          body: JSON.stringify({\n            model: 'gpt-3.5-turbo',\n            messages: [systemPrompt, ...chatHistory]\n          })\n        });\n    \n        const data = await response.json();\n        const reply = data.choices?.[0]?.message?.content || 'No se obtuvo respuesta.';\n    \n        removeLastBotThinking();\n        appendMessage('bot', reply);\n        \n        const ahora = new Date();\n        const timestamp = ahora.toISOString();\n\n        chatHistory.push({ role: 'user', content: message, time: timestamp });\n        chatHistory.push({ role: 'assistant', content: reply, time: timestamp });\n\n        localStorage.setItem('historialChat', JSON.stringify(chatHistory));\n        \n        if (continuousMode) {\n          speakText(reply);\n        }\n        chatBody.scrollTop = chatBody.scrollHeight;\n    \n      } catch (error) {\n        removeLastBotThinking();\n        appendMessage('bot', '\u274c Error al conectarse a la IA.');\n        console.error(error);\n      }\n    }\n\n    function appendMessage(sender, text) {\n      const messageDiv = document.createElement('div');\n      messageDiv.className = `message ${sender}`;\n    \n      \/\/ Crear avatar\n      const avatar = document.createElement('div');\n      avatar.className = 'message-avatar';\n    \n      if (sender === 'bot') {\n        const perfil = document.getElementById('profileSelect').value;\n        avatar.textContent = obtenerEmojiPorPerfil(perfil);\n      } else {\n        avatar.textContent = '\ud83e\uddd1'; \/\/ Puedes cambiarlo por un avatar personalizado del usuario\n      }\n    \n      \/\/ Crear mensaje\n      const messageText = document.createElement('p');\n      let mensajeFinal = text;\n    \n      if (sender === 'bot') {\n        const perfil = document.getElementById('profileSelect').value;\n        const nombreAsistente = obtenerNombreAsistente(perfil);\n        mensajeFinal = `${nombreAsistente} dice:\\n${text}`;\n      }\n    \n      messageText.innerText = mensajeFinal;\n    \n      \/\/ Timestamp\n      const time = document.createElement('span');\n      time.className = 'timestamp';\n      const ahora = new Date();\n      const hora = ahora.toLocaleTimeString('es-ES', { hour: '2-digit', minute: '2-digit' });\n      const fecha = ahora.toLocaleDateString('es-ES');\n      time.innerText = `${fecha} ${hora}`;\n    \n      messageDiv.appendChild(avatar);\n      messageDiv.appendChild(messageText);\n      messageDiv.appendChild(time);\n    \n      chatBody.appendChild(messageDiv);\n    }\n\n\n\n\n    function removeLastBotThinking() {\n      const messages = chatBody.querySelectorAll('.bot');\n      if (messages.length > 0) {\n        const lastBotMsg = messages[messages.length - 1];\n        if (lastBotMsg.textContent.includes('Pensando')) {\n          chatBody.removeChild(lastBotMsg);\n        }\n      }\n    }\n\n    function obtenerAjustesDeVoz(perfil) {\n      switch (perfil) {\n        case 'coach':\n          return { rate: 1.3, pitch: 1.2 }; \/\/ Energ\u00e9tico y motivador\n        case 'psicologo':\n          return { rate: 0.9, pitch: 1.0 }; \/\/ Calmo y neutral\n        case 'educador':\n          return { rate: 1.0, pitch: 1.0 }; \/\/ Claridad est\u00e1ndar\n        case 'ejecutivo_cuenta':\n          return { rate: 1.1, pitch: 1.0 }; \/\/ Profesional\n        case 'servicio_cliente':\n          return { rate: 1.1, pitch: 1.1 }; \/\/ Cordial y claro\n        case 'soporte_tecnico':\n          return { rate: 1.0, pitch: 0.95 }; \/\/ Neutro y t\u00e9cnico\n        case 'entrenador_personal':\n          return { rate: 1.25, pitch: 1.1 }; \/\/ Motivador, activo\n        case 'neurocientifico':\n          return { rate: 0.95, pitch: 0.95 }; \/\/ Preciso y pausado\n        case 'nutricionista':\n          return { rate: 1.05, pitch: 1.0 }; \/\/ C\u00e1lido y profesional\n        default:\n          return { rate: 1.1, pitch: 1.1 }; \/\/ Asistente general\n      }\n    }\n\n    function speakText(text) {\n      if (!continuousMode) return; \/\/ \u2190 Verificaci\u00f3n adicional por si se llama desde otro lugar\n    \n      const mensaje = new SpeechSynthesisUtterance(text);\n      const selectedIndex = voiceSelect.value || 0;\n      const voz = voces[selectedIndex] || voces.find(v => v.lang.includes('es'));\n      const perfilSeleccionado = document.getElementById('profileSelect').value;\n      const ajustes = obtenerAjustesDeVoz(perfilSeleccionado);\n      const vozStatus = document.getElementById('voz-status');\n    \n      mensaje.voice = voz;\n      mensaje.lang = voz?.lang || 'es-ES';\n      mensaje.rate = ajustes.rate;\n      mensaje.pitch = ajustes.pitch;\n    \n      mensaje.onstart = () => {\n        speaking = true;\n        vozStatus.style.display = 'block'; \/\/ Mostrar indicador\n        if (continuousMode && recognition) recognition.stop();\n      };\n    \n      mensaje.onend = () => {\n        speaking = false;\n        vozStatus.style.display = 'none'; \/\/ Ocultar indicador\n        if (continuousMode && recognition) recognition.start();\n      };\n    \n      speechSynthesis.speak(mensaje);\n    }\n\n\n    function probarVoz() {\n      const prueba = new SpeechSynthesisUtterance(\"Hola, soy tu asistente con voz. Estoy listo para ayudarte.\");\n      const selectedIndex = voiceSelect.value || 0;\n      const voz = voces[selectedIndex] || voces.find(v => v.lang.includes('es'));\n\n      prueba.voice = voz;\n      prueba.lang = voz?.lang || 'es-ES';\n      \n      const perfilSeleccionado = document.getElementById('profileSelect').value;\n      const ajustes = obtenerAjustesDeVoz(perfilSeleccionado);\n      prueba.rate = ajustes.rate;\n      prueba.pitch = ajustes.pitch;\n\n      speechSynthesis.speak(prueba);\n\n      setTimeout(() => {\n        if (!speechSynthesis.speaking) {\n          alert(\"\u26a0\ufe0f La voz no se reprodujo. Revisa el navegador o el volumen.\");\n        }\n      }, 2000);\n    }\n\n    micButton.addEventListener('click', () => {\n      recognition.start();\n      micButton.textContent = '\ud83c\udfa7';\n    });\n\n    recognition.onresult = (event) => {\n      const transcript = event.results[0][0].transcript;\n      userInput.value = transcript;\n\n      if (continuousMode) {\n        recognition.stop();\n        sendMessage(transcript);\n      }\n    };\n\n    recognition.onend = () => {\n      if (continuousMode && !speaking) {\n        recognition.start();\n      } else {\n        micButton.textContent = '\ud83c\udf99\ufe0f';\n      }\n    };\n\n    recognition.onerror = (event) => {\n      console.error('Reconocimiento de voz error:', event.error);\n      if (!continuousMode) {\n        micButton.textContent = '\ud83c\udf99\ufe0f';\n      }\n    };\n\n    toggleContinuous.addEventListener('click', () => {\n      continuousMode = !continuousMode;\n      toggleContinuous.textContent = continuousMode ? '\u23f9\ufe0f Detener Conversaci\u00f3n' : '\ud83c\udfa4 Activar Conversaci\u00f3n';\n\n      if (continuousMode) {\n        recognition.continuous = true;\n        recognition.start();\n      } else {\n        recognition.stop();\n        speechSynthesis.cancel();\n      }\n    });\n\n    userInput.addEventListener('keydown', (e) => {\n      if (e.key === 'Enter') sendMessage();\n    });\n    \n    function borrarHistorial() {\n      localStorage.removeItem('historialChat');\n      chatHistory = [];\n      chatBody.innerHTML = '';\n    }\n\n  <\/script>\n<\/body>\n<\/html>\n\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>Neo 2.0.3 Beta Asistente AI con Voz Continua Selecciona un perfil profesional del asistente: \ud83e\udd16Asistente General\ud83d\udc68\u200d\ud83c\udfeb Educador\ud83c\udfaf Coach\ud83e\udde0 Psic\u00f3logo\ud83d\udcbc Ejecutivo [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"site-sidebar-layout":"no-sidebar","site-content-layout":"","ast-site-content-layout":"full-width-container","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"disabled","ast-breadcrumbs-content":"","ast-featured-img":"disabled","footer-sml-layout":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"class_list":["post-178","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/neocloud05.com\/neo\/wp-json\/wp\/v2\/pages\/178","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/neocloud05.com\/neo\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/neocloud05.com\/neo\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/neocloud05.com\/neo\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/neocloud05.com\/neo\/wp-json\/wp\/v2\/comments?post=178"}],"version-history":[{"count":51,"href":"https:\/\/neocloud05.com\/neo\/wp-json\/wp\/v2\/pages\/178\/revisions"}],"predecessor-version":[{"id":252,"href":"https:\/\/neocloud05.com\/neo\/wp-json\/wp\/v2\/pages\/178\/revisions\/252"}],"wp:attachment":[{"href":"https:\/\/neocloud05.com\/neo\/wp-json\/wp\/v2\/media?parent=178"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}