From ce7d0e749dc5e98673835745146d6d3ba7bfafe5 Mon Sep 17 00:00:00 2001 From: Daniel Onyejesi Date: Sun, 29 Mar 2026 21:32:30 -0400 Subject: [PATCH] Fix LiteLLM STT: use axios directly instead of OpenAI SDK MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OpenAI SDK's audio.transcriptions.create() hangs with LiteLLM (no timeout, SDK-level incompatibility with multipart handling). Use axios + form-data directly with 120s timeout — same approach as ElevenLabs TTS. Handles both {text:"..."} and plain string responses. --- src/routes/transcribe.js | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/routes/transcribe.js b/src/routes/transcribe.js index fcb6d73..e0706b9 100644 --- a/src/routes/transcribe.js +++ b/src/routes/transcribe.js @@ -69,17 +69,27 @@ router.post('/transcribe', authMiddleware, upload.single('audio'), async (req, r } if (provider === 'litellm') { - if (!litellmClient) return res.status(400).json({ error: 'LiteLLM not configured. Set LITELLM_API_BASE.' }); + if (!process.env.LITELLM_API_BASE) return res.status(400).json({ error: 'LITELLM_API_BASE not set.' }); var sttModel = process.env.LITELLM_STT_MODEL || 'whisper-1'; - var file = new File([req.file.buffer], 'audio.webm', { type: req.file.mimetype || 'audio/webm' }); - var result = await litellmClient.audio.transcriptions.create({ - file: file, - model: sttModel, - language: 'en', - response_format: 'text', - prompt: 'Medical patient encounter. Pediatric. Clinical terms, diagnoses, medications.' + var FormData = require('form-data'); + var axios = require('axios'); + var form = new FormData(); + form.append('file', req.file.buffer, { + filename: 'audio.webm', + contentType: req.file.mimetype || 'audio/webm' }); - var text = typeof result === 'string' ? result : result.text; + form.append('model', sttModel); + form.append('language', 'en'); + form.append('response_format', 'json'); + form.append('prompt', 'Medical patient encounter. Pediatric. Clinical terms, diagnoses, medications.'); + var sttHeaders = Object.assign({}, form.getHeaders()); + if (process.env.LITELLM_API_KEY) sttHeaders['Authorization'] = 'Bearer ' + process.env.LITELLM_API_KEY; + var base = process.env.LITELLM_API_BASE.replace(/\/+$/, ''); + var sttResp = await axios.post(base + '/v1/audio/transcriptions', form, { + headers: sttHeaders, + timeout: 120000 + }); + var text = (sttResp.data && sttResp.data.text) ? sttResp.data.text : String(sttResp.data || ''); console.log('[Transcribe] LiteLLM (' + sttModel + ') done in ' + (Date.now() - startTime) + 'ms'); return res.json({ success: true, text: text, provider: 'litellm-stt', duration: Date.now() - startTime }); }