- TRANSCRIBE_PROVIDER=litellm routes audio to LiteLLM /audio/transcriptions - TTS_PROVIDER=litellm routes to LiteLLM /audio/speech - Both auto-detect when LITELLM_API_BASE is set (no extra config needed) - LITELLM_STT_MODEL (default: whisper-1), LITELLM_TTS_MODEL (default: tts-1) - LITELLM_TTS_VOICE (default: alloy) — alloy/echo/fable/onyx/nova/shimmer - ElevenLabs still works if ELEVENLABS_API_KEY is set and TTS_PROVIDER=elevenlabs - Health endpoint now reports tts provider
62 lines
2.5 KiB
JavaScript
62 lines
2.5 KiB
JavaScript
const express = require('express');
|
|
const router = express.Router();
|
|
const axios = require('axios');
|
|
const { litellmClient } = require('../utils/ai');
|
|
const { authMiddleware } = require('../middleware/auth');
|
|
|
|
// TTS_PROVIDER=litellm → LiteLLM proxy (routes to any OpenAI-compatible TTS)
|
|
// TTS_PROVIDER=elevenlabs → ElevenLabs (not HIPAA)
|
|
// (auto) LITELLM_API_BASE set → litellm
|
|
// (auto) ELEVENLABS_API_KEY set → elevenlabs
|
|
function getTTSProvider() {
|
|
var env = process.env.TTS_PROVIDER;
|
|
if (env === 'litellm') return 'litellm';
|
|
if (env === 'elevenlabs') return 'elevenlabs';
|
|
if (process.env.LITELLM_API_BASE && litellmClient) return 'litellm';
|
|
if (process.env.ELEVENLABS_API_KEY) return 'elevenlabs';
|
|
return 'none';
|
|
}
|
|
|
|
var ttsProvider = getTTSProvider();
|
|
console.log('🔊 TTS provider:', ttsProvider);
|
|
|
|
router.post('/text-to-speech', authMiddleware, async (req, res) => {
|
|
try {
|
|
var text = (req.body.text || '').substring(0, 5000);
|
|
if (!text) return res.status(400).json({ error: 'No text provided' });
|
|
|
|
if (ttsProvider === 'litellm') {
|
|
if (!litellmClient) return res.status(400).json({ error: 'LiteLLM not configured. Set LITELLM_API_BASE.' });
|
|
var ttsModel = process.env.LITELLM_TTS_MODEL || 'tts-1';
|
|
var ttsVoice = process.env.LITELLM_TTS_VOICE || 'alloy';
|
|
var mp3 = await litellmClient.audio.speech.create({
|
|
model: ttsModel,
|
|
voice: ttsVoice,
|
|
input: text,
|
|
response_format: 'mp3'
|
|
});
|
|
var buffer = Buffer.from(await mp3.arrayBuffer());
|
|
res.set('Content-Type', 'audio/mpeg');
|
|
return res.send(buffer);
|
|
}
|
|
|
|
if (ttsProvider === 'elevenlabs') {
|
|
if (!process.env.ELEVENLABS_API_KEY) return res.status(400).json({ error: 'ElevenLabs not configured' });
|
|
const response = await axios({
|
|
method: 'POST',
|
|
url: 'https://api.elevenlabs.io/v1/text-to-speech/pNInz6obpgDQGcFmaJgB',
|
|
headers: { 'xi-api-key': process.env.ELEVENLABS_API_KEY, 'Content-Type': 'application/json' },
|
|
data: { text: text, model_id: 'eleven_turbo_v2_5', voice_settings: { stability: 0.5, similarity_boost: 0.75 } },
|
|
responseType: 'arraybuffer'
|
|
});
|
|
res.set('Content-Type', 'audio/mpeg');
|
|
return res.send(Buffer.from(response.data));
|
|
}
|
|
|
|
res.status(400).json({ error: 'TTS not configured. Set LITELLM_API_BASE or ELEVENLABS_API_KEY.' });
|
|
} catch (err) {
|
|
res.status(500).json({ error: 'TTS failed: ' + err.message });
|
|
}
|
|
});
|
|
|
|
module.exports = router;
|