Spaces:
Runtime error
Runtime error
Upload folder using huggingface_hub
Browse files
chat.js
ADDED
@@ -0,0 +1,288 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// Variables globales
|
2 |
+
let currentMode = 'seguros';
|
3 |
+
let currentModel = 'Gemini 8b';
|
4 |
+
let currentTTS = 'EDGE';
|
5 |
+
let isListening = false;
|
6 |
+
let recognition = null;
|
7 |
+
|
8 |
+
function playAudio(button, text) {
|
9 |
+
const icon = button ? button.querySelector('i') : null;
|
10 |
+
text = text || (button ? button.getAttribute('data-text') : '');
|
11 |
+
|
12 |
+
if (!text) {
|
13 |
+
console.error('No text provided for audio');
|
14 |
+
return;
|
15 |
+
}
|
16 |
+
|
17 |
+
if (button) {
|
18 |
+
icon.className = 'fas fa-spinner fa-spin';
|
19 |
+
button.disabled = true;
|
20 |
+
}
|
21 |
+
|
22 |
+
fetch('/generate_audio', {
|
23 |
+
method: 'POST',
|
24 |
+
headers: {
|
25 |
+
'Content-Type': 'application/json',
|
26 |
+
},
|
27 |
+
body: JSON.stringify({
|
28 |
+
text: text,
|
29 |
+
model: currentTTS
|
30 |
+
})
|
31 |
+
})
|
32 |
+
.then(response => {
|
33 |
+
if (!response.ok) {
|
34 |
+
throw new Error(`HTTP error! status: ${response.status}`);
|
35 |
+
}
|
36 |
+
return response.json();
|
37 |
+
})
|
38 |
+
.then(data => {
|
39 |
+
if (data.audio_url) {
|
40 |
+
console.log(`Reproduciendo audio generado con modelo: ${data.model_used}`);
|
41 |
+
const audio = new Audio(window.location.origin + data.audio_url);
|
42 |
+
|
43 |
+
if (button) {
|
44 |
+
audio.onplay = () => {
|
45 |
+
icon.className = 'fas fa-pause';
|
46 |
+
};
|
47 |
+
|
48 |
+
audio.onended = () => {
|
49 |
+
icon.className = 'fas fa-play';
|
50 |
+
button.disabled = false;
|
51 |
+
};
|
52 |
+
}
|
53 |
+
|
54 |
+
audio.onerror = (e) => {
|
55 |
+
console.error('Error reproduciendo audio:', e);
|
56 |
+
if (button) {
|
57 |
+
icon.className = 'fas fa-play';
|
58 |
+
button.disabled = false;
|
59 |
+
}
|
60 |
+
appendBotMessage("Error reproduciendo el audio. Intente de nuevo.");
|
61 |
+
};
|
62 |
+
|
63 |
+
audio.play().catch(error => {
|
64 |
+
console.error('Error playing audio:', error);
|
65 |
+
if (button) {
|
66 |
+
icon.className = 'fas fa-play';
|
67 |
+
button.disabled = false;
|
68 |
+
}
|
69 |
+
appendBotMessage("Error reproduciendo el audio. Intente de nuevo.");
|
70 |
+
});
|
71 |
+
} else {
|
72 |
+
throw new Error('No audio URL in response');
|
73 |
+
}
|
74 |
+
})
|
75 |
+
.catch(error => {
|
76 |
+
console.error('Error:', error);
|
77 |
+
if (button) {
|
78 |
+
icon.className = 'fas fa-play';
|
79 |
+
button.disabled = false;
|
80 |
+
}
|
81 |
+
appendBotMessage(`Error generando el audio: ${error.message}`);
|
82 |
+
});
|
83 |
+
}
|
84 |
+
|
85 |
+
// Agregar esto para manejar nuevos mensajes din谩micamente
|
86 |
+
function appendBotMessage(message) {
|
87 |
+
const chatMessages = document.getElementById('chat-messages');
|
88 |
+
const messageDiv = document.createElement('div');
|
89 |
+
messageDiv.className = 'message bot-message';
|
90 |
+
messageDiv.innerHTML = `
|
91 |
+
<div class="message-content">${message}</div>
|
92 |
+
<button onclick="playAudio(this)" data-text="${message}" class="play-button">
|
93 |
+
<i class="fas fa-play"></i>
|
94 |
+
</button>
|
95 |
+
`;
|
96 |
+
chatMessages.appendChild(messageDiv);
|
97 |
+
}
|
98 |
+
|
99 |
+
async function sendMessage() {
|
100 |
+
const input = document.getElementById('user-input');
|
101 |
+
const text = input.value.trim();
|
102 |
+
|
103 |
+
if (text) {
|
104 |
+
// Agregar mensaje del usuario
|
105 |
+
const chatMessages = document.getElementById('chat-messages');
|
106 |
+
const userDiv = document.createElement('div');
|
107 |
+
userDiv.className = 'message user-message';
|
108 |
+
userDiv.textContent = text;
|
109 |
+
chatMessages.appendChild(userDiv);
|
110 |
+
|
111 |
+
// Limpiar input
|
112 |
+
input.value = '';
|
113 |
+
|
114 |
+
try {
|
115 |
+
// Enviar mensaje al backend con el modo actual
|
116 |
+
const response = await fetch('/chat', {
|
117 |
+
method: 'POST',
|
118 |
+
headers: {
|
119 |
+
'Content-Type': 'application/json',
|
120 |
+
},
|
121 |
+
body: JSON.stringify({
|
122 |
+
message: text,
|
123 |
+
mode: currentMode
|
124 |
+
})
|
125 |
+
});
|
126 |
+
|
127 |
+
const data = await response.json();
|
128 |
+
|
129 |
+
if (data.response) {
|
130 |
+
// Agregar respuesta del bot
|
131 |
+
appendBotMessage(data.response);
|
132 |
+
// Auto-reproducir respuesta
|
133 |
+
const lastButton = document.querySelector('.bot-message:last-child .play-button');
|
134 |
+
if (lastButton) {
|
135 |
+
playAudio(lastButton);
|
136 |
+
}
|
137 |
+
}
|
138 |
+
} catch (error) {
|
139 |
+
console.error('Error:', error);
|
140 |
+
appendBotMessage("Lo siento, hubo un error al procesar tu mensaje.");
|
141 |
+
}
|
142 |
+
}
|
143 |
+
}
|
144 |
+
|
145 |
+
async function changeMode(mode) {
|
146 |
+
try {
|
147 |
+
const response = await fetch('/change_mode', {
|
148 |
+
method: 'POST',
|
149 |
+
headers: {
|
150 |
+
'Content-Type': 'application/json',
|
151 |
+
},
|
152 |
+
body: JSON.stringify({ mode })
|
153 |
+
});
|
154 |
+
|
155 |
+
if (response.ok) {
|
156 |
+
currentMode = mode;
|
157 |
+
appendBotMessage(`Modo cambiado a: ${mode}`);
|
158 |
+
playAudio(null, `Modo de operaci贸n cambiado a ${mode}`);
|
159 |
+
}
|
160 |
+
} catch (error) {
|
161 |
+
console.error('Error changing mode:', error);
|
162 |
+
appendBotMessage("Error al cambiar el modo de operaci贸n");
|
163 |
+
}
|
164 |
+
}
|
165 |
+
|
166 |
+
async function changeModel(model) {
|
167 |
+
try {
|
168 |
+
const response = await fetch('/change_model', {
|
169 |
+
method: 'POST',
|
170 |
+
headers: {
|
171 |
+
'Content-Type': 'application/json',
|
172 |
+
},
|
173 |
+
body: JSON.stringify({ model })
|
174 |
+
});
|
175 |
+
|
176 |
+
if (response.ok) {
|
177 |
+
currentModel = model;
|
178 |
+
appendBotMessage(`Modelo de IA cambiado a: ${model}`);
|
179 |
+
playAudio(null, `Modelo de inteligencia artificial cambiado a ${model}`);
|
180 |
+
}
|
181 |
+
} catch (error) {
|
182 |
+
console.error('Error changing model:', error);
|
183 |
+
appendBotMessage("Error al cambiar el modelo de IA");
|
184 |
+
}
|
185 |
+
}
|
186 |
+
|
187 |
+
// Inicializar reconocimiento de voz
|
188 |
+
function initSpeechRecognition() {
|
189 |
+
if ('webkitSpeechRecognition' in window) {
|
190 |
+
recognition = new webkitSpeechRecognition();
|
191 |
+
recognition.continuous = true;
|
192 |
+
recognition.interimResults = true;
|
193 |
+
recognition.lang = 'es-ES';
|
194 |
+
|
195 |
+
recognition.onresult = function(event) {
|
196 |
+
const result = event.results[event.results.length - 1];
|
197 |
+
if (result.isFinal) {
|
198 |
+
const text = result.item(0).transcript;
|
199 |
+
document.getElementById('user-input').value = text;
|
200 |
+
sendMessage();
|
201 |
+
}
|
202 |
+
};
|
203 |
+
|
204 |
+
recognition.onerror = function(event) {
|
205 |
+
console.error('Error en reconocimiento:', event.error);
|
206 |
+
toggleVAD();
|
207 |
+
};
|
208 |
+
} else {
|
209 |
+
console.error('Reconocimiento de voz no soportado');
|
210 |
+
}
|
211 |
+
}
|
212 |
+
|
213 |
+
// Funci贸n para cambiar TTS
|
214 |
+
async function changeTTS(model) {
|
215 |
+
try {
|
216 |
+
const response = await fetch('/change_tts', {
|
217 |
+
method: 'POST',
|
218 |
+
headers: {
|
219 |
+
'Content-Type': 'application/json',
|
220 |
+
},
|
221 |
+
body: JSON.stringify({ model })
|
222 |
+
});
|
223 |
+
|
224 |
+
if (response.ok) {
|
225 |
+
currentTTS = model;
|
226 |
+
appendBotMessage(`Modelo de voz cambiado a: ${model}`);
|
227 |
+
playAudio(null, `Voz del sistema cambiada a ${model}`);
|
228 |
+
}
|
229 |
+
} catch (error) {
|
230 |
+
console.error('Error changing TTS:', error);
|
231 |
+
appendBotMessage("Error al cambiar el modelo de voz");
|
232 |
+
}
|
233 |
+
}
|
234 |
+
|
235 |
+
// Funci贸n para activar/desactivar VAD
|
236 |
+
function toggleVAD() {
|
237 |
+
const vadButton = document.querySelector('.vad-button');
|
238 |
+
const vadStatus = document.getElementById('vad-status');
|
239 |
+
|
240 |
+
if (!isListening) {
|
241 |
+
if (recognition) {
|
242 |
+
recognition.start();
|
243 |
+
isListening = true;
|
244 |
+
vadButton.classList.remove('inactive');
|
245 |
+
vadButton.classList.add('active');
|
246 |
+
vadButton.innerHTML = '<i class="fas fa-microphone"></i> Escuchando';
|
247 |
+
|
248 |
+
// Mostrar estado de escucha
|
249 |
+
vadStatus.classList.add('listening');
|
250 |
+
vadStatus.innerHTML = '馃帳 Escuchando...';
|
251 |
+
|
252 |
+
// Reproducir sonido de inicio
|
253 |
+
playAudio(null, "Sistema activado. Puede hablar.");
|
254 |
+
}
|
255 |
+
} else {
|
256 |
+
if (recognition) {
|
257 |
+
recognition.stop();
|
258 |
+
isListening = false;
|
259 |
+
vadButton.classList.remove('active');
|
260 |
+
vadButton.classList.add('inactive');
|
261 |
+
vadButton.innerHTML = '<i class="fas fa-microphone-slash"></i> Activar micr贸fono';
|
262 |
+
|
263 |
+
// Ocultar estado de escucha
|
264 |
+
vadStatus.classList.remove('listening');
|
265 |
+
vadStatus.innerHTML = '';
|
266 |
+
|
267 |
+
// Mensaje de desactivaci贸n
|
268 |
+
playAudio(null, "Sistema en pausa.");
|
269 |
+
}
|
270 |
+
}
|
271 |
+
}
|
272 |
+
|
273 |
+
// Inicializar cuando el documento est茅 listo
|
274 |
+
document.addEventListener('DOMContentLoaded', function() {
|
275 |
+
initSpeechRecognition();
|
276 |
+
// Iniciar VAD autom谩ticamente
|
277 |
+
setTimeout(() => {
|
278 |
+
toggleVAD(); // Activar el VAD al inicio
|
279 |
+
appendBotMessage("Sistema iniciado. Modelos cargados correctamente. Escuchando...");
|
280 |
+
}, 1000);
|
281 |
+
});
|
282 |
+
|
283 |
+
// Permitir enviar con Enter
|
284 |
+
document.getElementById('user-input')?.addEventListener('keypress', function(e) {
|
285 |
+
if (e.key === 'Enter') {
|
286 |
+
sendMessage();
|
287 |
+
}
|
288 |
+
});
|
main.js
ADDED
@@ -0,0 +1,384 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
let recognition = null;
|
2 |
+
let isProcessingSpeech = false;
|
3 |
+
let isPlayingAudio = false;
|
4 |
+
const STOP_WORDS = ['alto', 'detente', 'permiteme', 'callate', 'silencio'];
|
5 |
+
|
6 |
+
// Elementos del DOM
|
7 |
+
const chatBox = document.getElementById('chatBox');
|
8 |
+
const textInput = document.getElementById('textInput');
|
9 |
+
const sendTextButton = document.getElementById('sendText');
|
10 |
+
const modoSelect = document.getElementById('modoSelect');
|
11 |
+
const modeloSelect = document.getElementById('modeloSelect');
|
12 |
+
const vozSelect = document.getElementById('vozSelect');
|
13 |
+
const configForm = document.getElementById('configForm');
|
14 |
+
const statusLabel = document.getElementById('statusLabel');
|
15 |
+
|
16 |
+
// Manejar guardado de configuraci贸n
|
17 |
+
if (configForm) {
|
18 |
+
configForm.addEventListener('submit', async (e) => {
|
19 |
+
e.preventDefault();
|
20 |
+
const config = {
|
21 |
+
GOOGLE_API_KEY: document.getElementById('googleApiKey').value,
|
22 |
+
HUGGINGFACE_TOKEN: document.getElementById('huggingfaceToken').value,
|
23 |
+
OPENAI_API_KEY: document.getElementById('openaiApiKey').value
|
24 |
+
};
|
25 |
+
|
26 |
+
try {
|
27 |
+
const response = await fetch('/guardar_config', {
|
28 |
+
method: 'POST',
|
29 |
+
headers: {
|
30 |
+
'Content-Type': 'application/json'
|
31 |
+
},
|
32 |
+
body: JSON.stringify(config)
|
33 |
+
});
|
34 |
+
|
35 |
+
const data = await response.json();
|
36 |
+
if (data.success) {
|
37 |
+
alert('Configuraci贸n guardada exitosamente');
|
38 |
+
} else {
|
39 |
+
alert('Error al guardar la configuraci贸n: ' + data.error);
|
40 |
+
}
|
41 |
+
} catch (error) {
|
42 |
+
alert('Error al guardar la configuraci贸n: ' + error);
|
43 |
+
}
|
44 |
+
});
|
45 |
+
}
|
46 |
+
|
47 |
+
// Manejar cambio de modelo AI
|
48 |
+
if (modeloSelect) {
|
49 |
+
modeloSelect.addEventListener('change', async function() {
|
50 |
+
const response = await fetch('/cambiar_modelo', {
|
51 |
+
method: 'POST',
|
52 |
+
headers: {
|
53 |
+
'Content-Type': 'application/json'
|
54 |
+
},
|
55 |
+
body: JSON.stringify({ modelo: this.value })
|
56 |
+
});
|
57 |
+
|
58 |
+
const data = await response.json();
|
59 |
+
if (!data.success) {
|
60 |
+
alert('Error al cambiar el modelo: ' + data.error);
|
61 |
+
}
|
62 |
+
});
|
63 |
+
}
|
64 |
+
|
65 |
+
// Manejar cambio de modelo de voz
|
66 |
+
if (vozSelect) {
|
67 |
+
vozSelect.addEventListener('change', async function() {
|
68 |
+
const response = await fetch('/cambiar_voz', {
|
69 |
+
method: 'POST',
|
70 |
+
headers: {
|
71 |
+
'Content-Type': 'application/json'
|
72 |
+
},
|
73 |
+
body: JSON.stringify({ voz: this.value })
|
74 |
+
});
|
75 |
+
|
76 |
+
const data = await response.json();
|
77 |
+
if (!data.success) {
|
78 |
+
alert('Error al cambiar el modelo de voz: ' + data.error);
|
79 |
+
}
|
80 |
+
});
|
81 |
+
}
|
82 |
+
|
83 |
+
// Manejar cambio de modo
|
84 |
+
modoSelect.addEventListener('change', async function() {
|
85 |
+
const response = await fetch('/cambiar_modo', {
|
86 |
+
method: 'POST',
|
87 |
+
headers: {
|
88 |
+
'Content-Type': 'application/json'
|
89 |
+
},
|
90 |
+
body: JSON.stringify({ modo: this.value })
|
91 |
+
});
|
92 |
+
|
93 |
+
const data = await response.json();
|
94 |
+
if (data.success) {
|
95 |
+
addMessage(data.mensaje, 'bot');
|
96 |
+
if (data.audio) {
|
97 |
+
await playAudio(data.audio);
|
98 |
+
}
|
99 |
+
updateStatus('Esperando activaci贸n...');
|
100 |
+
}
|
101 |
+
});
|
102 |
+
|
103 |
+
// Funci贸n para verificar si el texto contiene palabras de parada
|
104 |
+
function containsStopWord(text) {
|
105 |
+
const lowerText = text.toLowerCase();
|
106 |
+
return STOP_WORDS.some(word => lowerText.includes(word));
|
107 |
+
}
|
108 |
+
|
109 |
+
// Funci贸n para reproducir audio
|
110 |
+
async function playAudio(base64Audio) {
|
111 |
+
try {
|
112 |
+
console.log('Preparando reproducci贸n de audio...');
|
113 |
+
isPlayingAudio = true;
|
114 |
+
|
115 |
+
// Detener completamente el reconocimiento
|
116 |
+
if (recognition) {
|
117 |
+
recognition.stop();
|
118 |
+
recognition.abort();
|
119 |
+
await new Promise(resolve => setTimeout(resolve, 500));
|
120 |
+
}
|
121 |
+
|
122 |
+
updateStatus('Reproduciendo respuesta...');
|
123 |
+
const audio = new Audio('data:audio/mp3;base64,' + base64Audio);
|
124 |
+
|
125 |
+
// Permitir interrupciones durante la reproducci贸n
|
126 |
+
recognition.start();
|
127 |
+
|
128 |
+
audio.onended = () => {
|
129 |
+
console.log('Audio reproducido completamente');
|
130 |
+
isPlayingAudio = false;
|
131 |
+
updateStatus('Escuchando...');
|
132 |
+
};
|
133 |
+
|
134 |
+
audio.play();
|
135 |
+
|
136 |
+
} catch (error) {
|
137 |
+
console.error('Error reproduciendo audio:', error);
|
138 |
+
isPlayingAudio = false;
|
139 |
+
updateStatus('Error al reproducir audio. Escuchando...');
|
140 |
+
if (!isProcessingSpeech) {
|
141 |
+
recognition.start();
|
142 |
+
}
|
143 |
+
}
|
144 |
+
}
|
145 |
+
|
146 |
+
// Funci贸n para agregar mensaje al chat
|
147 |
+
function addMessage(text, sender) {
|
148 |
+
if (!text || !sender) {
|
149 |
+
console.error('Error: texto o remitente faltante', { text, sender });
|
150 |
+
return;
|
151 |
+
}
|
152 |
+
|
153 |
+
const chatBox = document.getElementById('chatBox');
|
154 |
+
if (!chatBox) {
|
155 |
+
console.error('Error cr铆tico: No se encontr贸 el elemento chatBox');
|
156 |
+
return;
|
157 |
+
}
|
158 |
+
|
159 |
+
try {
|
160 |
+
console.log('Agregando mensaje al chat:', { text, sender });
|
161 |
+
|
162 |
+
const messageDiv = document.createElement('div');
|
163 |
+
messageDiv.className = `message ${sender}-message`;
|
164 |
+
|
165 |
+
const iconSpan = document.createElement('span');
|
166 |
+
iconSpan.className = 'message-icon';
|
167 |
+
iconSpan.textContent = sender === 'user' ? '馃懁' : '馃';
|
168 |
+
|
169 |
+
const textSpan = document.createElement('span');
|
170 |
+
textSpan.className = 'message-text';
|
171 |
+
textSpan.textContent = text;
|
172 |
+
|
173 |
+
messageDiv.appendChild(iconSpan);
|
174 |
+
messageDiv.appendChild(textSpan);
|
175 |
+
chatBox.appendChild(messageDiv);
|
176 |
+
|
177 |
+
// Hacer scroll al 煤ltimo mensaje
|
178 |
+
chatBox.scrollTop = chatBox.scrollHeight;
|
179 |
+
|
180 |
+
console.log('Mensaje agregado exitosamente');
|
181 |
+
} catch (error) {
|
182 |
+
console.error('Error al agregar mensaje:', error);
|
183 |
+
}
|
184 |
+
}
|
185 |
+
|
186 |
+
// Agregar m煤ltiples mensajes
|
187 |
+
function addMessages(messages) {
|
188 |
+
messages.forEach(msg => {
|
189 |
+
addMessage(msg.text, msg.sender);
|
190 |
+
});
|
191 |
+
}
|
192 |
+
|
193 |
+
// Funci贸n para actualizar el estado
|
194 |
+
function updateStatus(text) {
|
195 |
+
const statusLabel = document.getElementById('statusLabel');
|
196 |
+
if (statusLabel) {
|
197 |
+
statusLabel.textContent = text;
|
198 |
+
statusLabel.style.display = 'block'; // Asegurar que sea visible
|
199 |
+
console.log('Estado actualizado:', text);
|
200 |
+
} else {
|
201 |
+
console.error('No se encontr贸 el elemento statusLabel');
|
202 |
+
}
|
203 |
+
}
|
204 |
+
|
205 |
+
// Funci贸n para procesar la respuesta del servidor
|
206 |
+
async function processServerResponse(data, userText) {
|
207 |
+
try {
|
208 |
+
console.log('Procesando respuesta del servidor:', data);
|
209 |
+
|
210 |
+
// Mostrar el mensaje del usuario
|
211 |
+
addMessage(userText, 'user');
|
212 |
+
|
213 |
+
if (data.success && data.texto) {
|
214 |
+
// Mostrar la respuesta del bot
|
215 |
+
console.log('Mostrando respuesta del bot:', data.texto);
|
216 |
+
addMessage(data.texto, 'bot');
|
217 |
+
|
218 |
+
// Reproducir el audio si existe y no se dijo una palabra de parada
|
219 |
+
if (data.audio && !containsStopWord(userText)) {
|
220 |
+
await playAudio(data.audio);
|
221 |
+
}
|
222 |
+
} else {
|
223 |
+
const errorMessage = data.error || 'Error desconocido';
|
224 |
+
console.error('Error en la respuesta:', errorMessage);
|
225 |
+
addMessage('Lo siento, hubo un error: ' + errorMessage, 'bot');
|
226 |
+
}
|
227 |
+
} catch (error) {
|
228 |
+
console.error('Error procesando respuesta:', error);
|
229 |
+
addMessage('Lo siento, ocurri贸 un error inesperado.', 'bot');
|
230 |
+
}
|
231 |
+
}
|
232 |
+
|
233 |
+
// Inicializar reconocimiento de voz
|
234 |
+
function initializeSpeechRecognition() {
|
235 |
+
if ('webkitSpeechRecognition' in window) {
|
236 |
+
recognition = new webkitSpeechRecognition();
|
237 |
+
recognition.continuous = true; // Cambiar a true para permitir interrupciones
|
238 |
+
recognition.interimResults = true;
|
239 |
+
recognition.lang = 'es-ES';
|
240 |
+
|
241 |
+
recognition.onstart = function() {
|
242 |
+
console.log('Reconocimiento de voz iniciado');
|
243 |
+
updateStatus('Escuchando...');
|
244 |
+
};
|
245 |
+
|
246 |
+
recognition.onend = function() {
|
247 |
+
console.log('Reconocimiento de voz terminado');
|
248 |
+
if (!isProcessingSpeech) {
|
249 |
+
console.log('Reiniciando reconocimiento...');
|
250 |
+
updateStatus('Escuchando...');
|
251 |
+
setTimeout(() => recognition.start(), 500);
|
252 |
+
}
|
253 |
+
};
|
254 |
+
|
255 |
+
recognition.onerror = function(event) {
|
256 |
+
console.error('Error en reconocimiento de voz:', event.error);
|
257 |
+
if (!isProcessingSpeech && event.error !== 'no-speech') {
|
258 |
+
setTimeout(() => recognition.start(), 1000);
|
259 |
+
}
|
260 |
+
};
|
261 |
+
|
262 |
+
recognition.onresult = async function(event) {
|
263 |
+
let finalTranscript = '';
|
264 |
+
let interimTranscript = '';
|
265 |
+
|
266 |
+
for (let i = event.resultIndex; i < event.results.length; ++i) {
|
267 |
+
const transcript = event.results[i][0].transcript;
|
268 |
+
|
269 |
+
if (event.results[i].isFinal) {
|
270 |
+
finalTranscript = transcript;
|
271 |
+
console.log('Texto final:', finalTranscript);
|
272 |
+
|
273 |
+
// Verificar si es una palabra de parada
|
274 |
+
if (containsStopWord(finalTranscript)) {
|
275 |
+
console.log('Palabra de parada detectada, deteniendo audio...');
|
276 |
+
const audio = document.querySelector('audio');
|
277 |
+
if (audio) {
|
278 |
+
audio.pause();
|
279 |
+
audio.currentTime = 0;
|
280 |
+
}
|
281 |
+
isPlayingAudio = false;
|
282 |
+
updateStatus('Audio detenido. Escuchando...');
|
283 |
+
continue;
|
284 |
+
}
|
285 |
+
|
286 |
+
if (!isProcessingSpeech) {
|
287 |
+
isProcessingSpeech = true;
|
288 |
+
updateStatus('Procesando...');
|
289 |
+
|
290 |
+
try {
|
291 |
+
console.log('Enviando texto al servidor:', finalTranscript);
|
292 |
+
const response = await fetch('/procesar_voz', {
|
293 |
+
method: 'POST',
|
294 |
+
headers: {
|
295 |
+
'Content-Type': 'application/json'
|
296 |
+
},
|
297 |
+
body: JSON.stringify({ texto: finalTranscript })
|
298 |
+
});
|
299 |
+
|
300 |
+
const data = await response.json();
|
301 |
+
console.log('Respuesta recibida:', data);
|
302 |
+
await processServerResponse(data, finalTranscript);
|
303 |
+
|
304 |
+
} catch (error) {
|
305 |
+
console.error('Error al procesar voz:', error);
|
306 |
+
updateStatus('Error de conexi贸n. Escuchando...');
|
307 |
+
addMessage('Lo siento, hubo un error de conexi贸n.', 'bot');
|
308 |
+
}
|
309 |
+
|
310 |
+
isProcessingSpeech = false;
|
311 |
+
}
|
312 |
+
} else {
|
313 |
+
interimTranscript = transcript;
|
314 |
+
updateStatus(`Escuchando: ${interimTranscript}`);
|
315 |
+
}
|
316 |
+
}
|
317 |
+
};
|
318 |
+
|
319 |
+
// Iniciar reconocimiento
|
320 |
+
recognition.start();
|
321 |
+
updateStatus('Escuchando...');
|
322 |
+
|
323 |
+
} else {
|
324 |
+
console.error('El reconocimiento de voz no est谩 soportado en este navegador');
|
325 |
+
alert('Tu navegador no soporta el reconocimiento de voz. Por favor, usa Chrome.');
|
326 |
+
updateStatus('Reconocimiento de voz no soportado');
|
327 |
+
}
|
328 |
+
}
|
329 |
+
|
330 |
+
// Manejar env铆o de texto
|
331 |
+
sendTextButton.addEventListener('click', async () => {
|
332 |
+
const text = textInput.value.trim();
|
333 |
+
if (text) {
|
334 |
+
updateStatus('Procesando...');
|
335 |
+
addMessage(text, 'user');
|
336 |
+
|
337 |
+
try {
|
338 |
+
const response = await fetch('/procesar_voz', {
|
339 |
+
method: 'POST',
|
340 |
+
headers: {
|
341 |
+
'Content-Type': 'application/json'
|
342 |
+
},
|
343 |
+
body: JSON.stringify({ texto: text })
|
344 |
+
});
|
345 |
+
|
346 |
+
const data = await response.json();
|
347 |
+
await processServerResponse(data, text);
|
348 |
+
|
349 |
+
textInput.value = '';
|
350 |
+
updateStatus('Esperando activaci贸n...');
|
351 |
+
} catch (error) {
|
352 |
+
console.error('Error:', error);
|
353 |
+
updateStatus('Error de conexi贸n');
|
354 |
+
}
|
355 |
+
}
|
356 |
+
});
|
357 |
+
|
358 |
+
// Inicializar cuando se carga la p谩gina
|
359 |
+
document.addEventListener('DOMContentLoaded', () => {
|
360 |
+
console.log('P谩gina cargada, verificando elementos...');
|
361 |
+
|
362 |
+
// Verificar que existan los elementos necesarios
|
363 |
+
const chatBox = document.getElementById('chatBox');
|
364 |
+
const statusLabel = document.getElementById('statusLabel');
|
365 |
+
|
366 |
+
if (!chatBox) {
|
367 |
+
console.error('Error cr铆tico: No se encontr贸 el elemento chatBox');
|
368 |
+
return;
|
369 |
+
}
|
370 |
+
|
371 |
+
if (!statusLabel) {
|
372 |
+
console.error('Error cr铆tico: No se encontr贸 el elemento statusLabel');
|
373 |
+
return;
|
374 |
+
}
|
375 |
+
|
376 |
+
// Asegurar que el statusLabel sea visible
|
377 |
+
statusLabel.style.display = 'block';
|
378 |
+
|
379 |
+
console.log('Elementos encontrados, inicializando reconocimiento de voz...');
|
380 |
+
initializeSpeechRecognition();
|
381 |
+
|
382 |
+
// Agregar mensaje de bienvenida
|
383 |
+
addMessage('隆Hola! Soy tu asistente virtual. 驴En qu茅 puedo ayudarte?', 'bot');
|
384 |
+
});
|