Initial FRIDAY Voice Console scaffold

This commit is contained in:
2026-06-25 07:24:34 -05:00
commit c2a304642a
17 changed files with 997 additions and 0 deletions
+98
View File
@@ -0,0 +1,98 @@
const recordButton = document.getElementById('recordButton');
const sendTextButton = document.getElementById('sendTextButton');
const textInput = document.getElementById('textInput');
const statusEl = document.getElementById('status');
const transcriptEl = document.getElementById('transcript');
const replyEl = document.getElementById('reply');
const audioReply = document.getElementById('audioReply');
let recorder;
let chunks = [];
function setStatus(text) {
statusEl.textContent = text;
}
async function askFriday(text) {
setStatus('Asking FRIDAY...');
const response = await fetch('/api/ask', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text, speaker_id: null, confidence: 0 })
});
if (!response.ok) throw new Error(await response.text());
const data = await response.json();
replyEl.textContent = data.reply;
setStatus(`Authorization: ${data.authorization_level}; ${data.reason}`);
await speak(data.reply);
}
async function speak(text) {
const response = await fetch('/api/speak', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text })
});
if (!response.ok) return;
const blob = await response.blob();
audioReply.src = URL.createObjectURL(blob);
await audioReply.play().catch(() => {});
}
async function transcribe(blob) {
setStatus('Transcribing...');
const form = new FormData();
form.append('audio', blob, 'utterance.webm');
const response = await fetch('/api/transcribe', {
method: 'POST',
headers: { 'Content-Type': 'audio/webm' },
body: blob
});
if (!response.ok) throw new Error(await response.text());
const data = await response.json();
transcriptEl.textContent = data.text;
textInput.value = data.text;
return data.text;
}
async function startRecording() {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
chunks = [];
recorder = new MediaRecorder(stream, { mimeType: 'audio/webm' });
recorder.addEventListener('dataavailable', event => {
if (event.data.size > 0) chunks.push(event.data);
});
recorder.addEventListener('stop', async () => {
stream.getTracks().forEach(track => track.stop());
recordButton.classList.remove('recording');
recordButton.textContent = 'Hold to Talk';
try {
const text = await transcribe(new Blob(chunks, { type: 'audio/webm' }));
if (text.trim()) await askFriday(text);
} catch (error) {
setStatus(`Error: ${error.message}`);
}
});
recorder.start();
recordButton.classList.add('recording');
recordButton.textContent = 'Listening... release to send';
setStatus('Listening...');
}
function stopRecording() {
if (recorder && recorder.state === 'recording') recorder.stop();
}
recordButton.addEventListener('pointerdown', startRecording);
recordButton.addEventListener('pointerup', stopRecording);
recordButton.addEventListener('pointerleave', stopRecording);
sendTextButton.addEventListener('click', async () => {
const text = textInput.value.trim();
if (!text) return;
transcriptEl.textContent = text;
try {
await askFriday(text);
} catch (error) {
setStatus(`Error: ${error.message}`);
}
});