Fix speech-to-text with Groq Whisper

This commit is contained in:
Chandu
2026-07-25 11:55:40 -04:00
parent 7a56c88907
commit 41429206ac
6 changed files with 160 additions and 26 deletions
+50 -20
View File
@@ -7,6 +7,7 @@ const googleTTS = require('google-tts-api');
require('dotenv').config();
const HUGGINGFACE_API_TOKEN = process.env.HUGGINGFACEHUB_API_TOKEN || process.env.HF_API_TOKEN;
const GROQ_API_KEY = process.env.GROQ_API_KEY;
const nllbLangMap = {
'en': 'eng_Latn',
'es': 'spa_Latn',
@@ -77,7 +78,7 @@ function loadSslOptions() {
// Generate a temporary self-signed certificate for localhost
console.warn('No SSL cert/key provided. Generating a temporary self-signed certificate for https://localhost:' + SSL_PORT);
const attrs = [{ name: 'commonName', value: 'localhost' }];
const pems = selfsigned.generate(attrs, { days: 365 });
const pems = selfsigned.generate(attrs, { days: 365, keySize: 2048 });
return { key: pems.private, cert: pems.cert };
}
@@ -332,7 +333,25 @@ const translateViaLibreTranslate = async (text, sourceLang, targetLang) => {
}
};
// Speech-to-text endpoint using Hugging Face Whisper
function getTranscriptionFilename(contentType) {
const mimeType = (contentType || 'audio/webm').split(';')[0].trim().toLowerCase();
if (mimeType.includes('mp4') || mimeType.includes('m4a') || mimeType.includes('aac')) {
return 'audio.mp4';
}
if (mimeType.includes('webm')) {
return 'audio.webm';
}
if (mimeType.includes('wav')) {
return 'audio.wav';
}
return 'audio.webm';
}
// Speech-to-text endpoint using Groq Whisper
app.post('/api/transcribe', async (req, res) => {
try {
const audioBuffer = req.body;
@@ -340,29 +359,40 @@ app.post('/api/transcribe', async (req, res) => {
return res.status(400).json({ error: 'No audio provided' });
}
if (!HUGGINGFACE_API_TOKEN) {
return res.status(500).json({ error: 'Hugging Face token not configured' });
if (!GROQ_API_KEY) {
return res.status(503).json({ error: 'Speech-to-text is temporarily unavailable: GROQ API key is not configured.' });
}
const contentType = req.headers['content-type'] || 'application/octet-stream';
const response = await axios.post(
'https://api-inference.huggingface.co/models/openai/whisper-large-v3',
audioBuffer,
{
headers: {
Authorization: `Bearer ${HUGGINGFACE_API_TOKEN}`,
'Content-Type': contentType
},
timeout: 60000
}
);
const contentType = req.headers['content-type'] || 'audio/webm';
const mimeType = contentType.split(';')[0].trim().toLowerCase();
const filename = getTranscriptionFilename(contentType);
const formData = new FormData();
const data = response.data;
const transcript = Array.isArray(data) ? data[0]?.text : data?.text || '';
res.json({ transcript });
formData.append('file', new Blob([audioBuffer], { type: mimeType }), filename);
formData.append('model', 'whisper-large-v3-turbo');
formData.append('response_format', 'json');
const response = await fetch('https://api.groq.com/openai/v1/audio/transcriptions', {
method: 'POST',
headers: {
Authorization: `Bearer ${GROQ_API_KEY}`
},
body: formData
});
const data = await response.json().catch(() => null);
if (!response.ok) {
const errorMessage = data?.error?.message || data?.error || 'Speech-to-text failed';
console.error('Groq speech-to-text error:', errorMessage);
return res.status(response.status).json({ error: errorMessage });
}
const transcript = data?.text || '';
return res.json({ transcript });
} catch (error) {
console.error('Speech-to-text error:', error.message || error);
res.status(500).json({ error: 'Speech-to-text failed' });
return res.status(500).json({ error: 'Speech-to-text failed' });
}
});