const { getLiteLLMHeaders } = require('./litellm'); function parseList(value) { return String(value || '') .split(',') .map(function(item) { return item.trim(); }) .filter(Boolean); } var KITTEN_TTS_VOICES = ['Bella', 'Jasper', 'Luna', 'Bruno', 'Rosie', 'Hugo', 'Kiki', 'Leo']; var SUPERTONIC_TTS_VOICES = ['F1', 'F2', 'F3', 'F4', 'F5', 'M1', 'M2', 'M3', 'M4', 'M5']; var GROQ_ORPHEUS_ENGLISH_VOICES = ['autumn', 'diana', 'hannah', 'austin', 'daniel', 'troy']; var GROQ_ORPHEUS_ARABIC_VOICES = ['abdullah', 'fahad', 'sultan', 'lulwa', 'noura', 'aisha']; function uniqueList(values) { var seen = new Set(); return (values || []).filter(function(value) { if (!value || seen.has(value)) return false; seen.add(value); return true; }); } function getTTSProvider() { var env = process.env.TTS_PROVIDER; if (env === 'litellm') return 'litellm'; if (process.env.LITELLM_API_BASE) return 'litellm'; return 'none'; } function getTTSEnvProvider() { return process.env.TTS_PROVIDER || 'auto'; } function getTTSVoiceLists() { return { litellm: uniqueList(parseList(process.env.LITELLM_TTS_VOICES).concat(KITTEN_TTS_VOICES, SUPERTONIC_TTS_VOICES, GROQ_ORPHEUS_ENGLISH_VOICES, GROQ_ORPHEUS_ARABIC_VOICES)) }; } function getLiteLLMTTSModelFamily(model) { var id = String(model || '').toLowerCase(); if (id === 'local-kitten-tts') return 'kitten'; if (id === 'local-supertonic-tts') return 'supertonic'; if (id === 'local-kokoro-tts') return 'kokoro'; if (id === 'groq-orpheus-english' || id === 'canopylabs/orpheus-v1-english') return 'groq-orpheus-english'; if (id === 'groq-orpheus-arabic-saudi' || id === 'canopylabs/orpheus-arabic-saudi') return 'groq-orpheus-arabic'; return 'unknown'; } function getLiteLLMTTSRequestOptions(model) { var family = getLiteLLMTTSModelFamily(model); if (family === 'groq-orpheus-english' || family === 'groq-orpheus-arabic') { return { response_format: 'wav' }; } return {}; } function isLiteLLMTTSVoiceCompatible(model, voice) { if (!voice) return true; var family = getLiteLLMTTSModelFamily(model); if (family === 'kitten') return KITTEN_TTS_VOICES.indexOf(voice) !== -1; if (family === 'supertonic') return SUPERTONIC_TTS_VOICES.indexOf(voice) !== -1; if (family === 'groq-orpheus-english') return GROQ_ORPHEUS_ENGLISH_VOICES.indexOf(String(voice).toLowerCase()) !== -1; if (family === 'groq-orpheus-arabic') return GROQ_ORPHEUS_ARABIC_VOICES.indexOf(String(voice).toLowerCase()) !== -1; if (family === 'kokoro') { return KITTEN_TTS_VOICES.indexOf(voice) === -1 && SUPERTONIC_TTS_VOICES.indexOf(voice) === -1 && GROQ_ORPHEUS_ENGLISH_VOICES.indexOf(String(voice).toLowerCase()) === -1 && GROQ_ORPHEUS_ARABIC_VOICES.indexOf(String(voice).toLowerCase()) === -1; } return true; } function getLiteLLMTTSVoicesForModel(model, opts) { opts = opts || {}; var family = getLiteLLMTTSModelFamily(model); var voices = []; if (family === 'kitten') voices = KITTEN_TTS_VOICES.slice(); else if (family === 'supertonic') voices = SUPERTONIC_TTS_VOICES.slice(); else if (family === 'groq-orpheus-english') voices = GROQ_ORPHEUS_ENGLISH_VOICES.slice(); else if (family === 'groq-orpheus-arabic') voices = GROQ_ORPHEUS_ARABIC_VOICES.slice(); else voices = parseList(process.env.LITELLM_TTS_VOICES); [opts.currentVoice, process.env.LITELLM_TTS_VOICE].forEach(function(voice) { if (isLiteLLMTTSVoiceCompatible(model, voice)) voices.push(voice); }); return uniqueList(voices.filter(function(voice) { return isLiteLLMTTSVoiceCompatible(model, voice); })); } function isLiteLLMTTSModel(model) { var mode = model && model.model_info && model.model_info.mode ? String(model.model_info.mode) : ''; return mode === 'audio_speech'; } function getLiteLLMTTSModels(models) { return (models || []) .filter(isLiteLLMTTSModel) .map(function(model) { return model && (model.id || model.model_name) ? (model.id || model.model_name) : String(model || ''); }); } function pushUniqueTTSItem(items, item) { if (!item || !item.id) return; if (items.some(function(existing) { return existing.id === item.id && existing.kind === item.kind; })) return; items.push(item); } function getLiteLLMTTSDiscoveryItems(models, opts) { opts = opts || {}; var items = []; getLiteLLMTTSModels(models).forEach(function(id) { pushUniqueTTSItem(items, { id: id, name: id, source: 'gateway-api', kind: 'model' }); }); if (opts.currentModel) { pushUniqueTTSItem(items, { id: opts.currentModel, name: opts.currentModel, source: 'configured-model', kind: 'model' }); } if (opts.currentVoice) { pushUniqueTTSItem(items, { id: opts.currentVoice, name: opts.currentVoice, source: 'configured-voice', kind: 'voice' }); } getTTSVoiceLists().litellm.forEach(function(voice) { pushUniqueTTSItem(items, { id: voice, name: voice, source: 'configured-voice-list', kind: 'voice' }); }); if (getLiteLLMTTSModels(models).indexOf('local-kitten-tts') !== -1 || opts.currentModel === 'local-kitten-tts') { KITTEN_TTS_VOICES.forEach(function(voice) { pushUniqueTTSItem(items, { id: voice, name: 'Kitten ' + voice, source: 'local-kitten-tts', kind: 'voice' }); }); } if (getLiteLLMTTSModels(models).indexOf('local-supertonic-tts') !== -1 || opts.currentModel === 'local-supertonic-tts') { SUPERTONIC_TTS_VOICES.forEach(function(voice) { pushUniqueTTSItem(items, { id: voice, name: 'Supertonic ' + voice, source: 'local-supertonic-tts', kind: 'voice' }); }); } if (getLiteLLMTTSModels(models).indexOf('groq-orpheus-english') !== -1 || opts.currentModel === 'groq-orpheus-english') { GROQ_ORPHEUS_ENGLISH_VOICES.forEach(function(voice) { pushUniqueTTSItem(items, { id: voice, name: 'Groq Orpheus ' + voice, source: 'groq-orpheus-english', kind: 'voice' }); }); } if (getLiteLLMTTSModels(models).indexOf('groq-orpheus-arabic-saudi') !== -1 || opts.currentModel === 'groq-orpheus-arabic-saudi') { GROQ_ORPHEUS_ARABIC_VOICES.forEach(function(voice) { pushUniqueTTSItem(items, { id: voice, name: 'Groq Orpheus Arabic ' + voice, source: 'groq-orpheus-arabic-saudi', kind: 'voice' }); }); } return items; } module.exports = { getTTSEnvProvider, getLiteLLMTTSDiscoveryItems, getLiteLLMHeaders, getLiteLLMTTSModels, getLiteLLMTTSRequestOptions, getLiteLLMTTSVoicesForModel, getTTSProvider, getTTSVoiceLists, isLiteLLMTTSVoiceCompatible, isLiteLLMTTSModel };