Skip to content

Commit 34d569d

Browse files
committed
refac
1 parent e709d68 commit 34d569d

1 file changed

Lines changed: 95 additions & 80 deletions

File tree

backend/open_webui/routers/audio.py

Lines changed: 95 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import uuid
66
import html
77
import base64
8-
from functools import lru_cache
98
from pydub import AudioSegment
109
from pydub.silence import split_on_silence
1110
from concurrent.futures import ThreadPoolExecutor
@@ -421,7 +420,7 @@ async def speech(request: Request, user=Depends(get_verified_user)):
421420
elif request.app.state.config.TTS_ENGINE == 'elevenlabs':
422421
voice_id = payload.get('voice', '')
423422

424-
if voice_id not in get_available_voices(request):
423+
if voice_id not in await get_available_voices(request):
425424
raise HTTPException(
426425
status_code=400,
427426
detail='Invalid voice id',
@@ -1295,65 +1294,79 @@ async def transcription(
12951294
)
12961295

12971296

1298-
def get_available_models(request: Request) -> list[dict]:
1297+
async def get_available_models(request: Request) -> list[dict]:
12991298
available_models = []
13001299
if request.app.state.config.TTS_ENGINE == 'openai':
13011300
# Use custom endpoint if not using the official OpenAI API URL
13021301
if not request.app.state.config.TTS_OPENAI_API_BASE_URL.startswith('https://api.openai.com'):
1303-
try:
1304-
response = requests.get(
1305-
f'{request.app.state.config.TTS_OPENAI_API_BASE_URL}/audio/models',
1306-
timeout=AIOHTTP_CLIENT_TIMEOUT_MODEL_LIST,
1307-
)
1308-
response.raise_for_status()
1309-
data = response.json()
1310-
available_models = data.get('models', [])
1311-
except Exception as e:
1312-
log.error(f'Error fetching models from custom endpoint: {str(e)}')
1313-
available_models = [{'id': 'tts-1'}, {'id': 'tts-1-hd'}]
1302+
timeout = aiohttp.ClientTimeout(total=AIOHTTP_CLIENT_TIMEOUT_MODEL_LIST)
1303+
async with aiohttp.ClientSession(timeout=timeout, trust_env=True) as session:
1304+
try:
1305+
async with session.get(
1306+
f'{request.app.state.config.TTS_OPENAI_API_BASE_URL}/audio/models',
1307+
) as response:
1308+
response.raise_for_status()
1309+
data = await response.json()
1310+
available_models = data.get('models', [])
1311+
except Exception as e:
1312+
log.debug(f'/audio/models not available, trying /models fallback: {str(e)}')
1313+
# Fallback to standard OpenAI-compatible /models endpoint
1314+
# (used by KokoroTTS and similar custom TTS servers)
1315+
try:
1316+
async with session.get(
1317+
f'{request.app.state.config.TTS_OPENAI_API_BASE_URL}/models',
1318+
) as response:
1319+
response.raise_for_status()
1320+
data = await response.json()
1321+
# OpenAI /models returns {"data": [...]}, /audio/models returns {"models": [...]}
1322+
available_models = data.get('data', data.get('models', []))
1323+
except Exception as e2:
1324+
log.error(f'Error fetching models from custom endpoint: {str(e2)}')
1325+
available_models = [{'id': 'tts-1'}, {'id': 'tts-1-hd'}]
13141326
else:
13151327
available_models = [{'id': 'tts-1'}, {'id': 'tts-1-hd'}]
13161328
elif request.app.state.config.TTS_ENGINE == 'elevenlabs':
13171329
try:
1318-
response = requests.get(
1319-
f'{ELEVENLABS_API_BASE_URL}/v1/models',
1320-
headers={
1321-
'xi-api-key': request.app.state.config.TTS_API_KEY,
1322-
'Content-Type': 'application/json',
1323-
},
1324-
timeout=5,
1325-
)
1326-
response.raise_for_status()
1327-
models = response.json()
1328-
1329-
available_models = [{'name': model['name'], 'id': model['model_id']} for model in models]
1330-
except requests.RequestException as e:
1331-
log.error(f'Error fetching voices: {str(e)}')
1330+
timeout = aiohttp.ClientTimeout(total=5)
1331+
async with aiohttp.ClientSession(timeout=timeout, trust_env=True) as session:
1332+
async with session.get(
1333+
f'{ELEVENLABS_API_BASE_URL}/v1/models',
1334+
headers={
1335+
'xi-api-key': request.app.state.config.TTS_API_KEY,
1336+
'Content-Type': 'application/json',
1337+
},
1338+
) as response:
1339+
response.raise_for_status()
1340+
models = await response.json()
1341+
available_models = [{'name': model['name'], 'id': model['model_id']} for model in models]
1342+
except Exception as e:
1343+
log.error(f'Error fetching models: {str(e)}')
13321344
elif request.app.state.config.TTS_ENGINE == 'mistral':
13331345
available_models = [{'id': 'mistral-tts-latest'}]
13341346
return available_models
13351347

13361348

13371349
@router.get('/models')
13381350
async def get_models(request: Request, user=Depends(get_verified_user)):
1339-
return {'models': get_available_models(request)}
1351+
return {'models': await get_available_models(request)}
13401352

13411353

1342-
def get_available_voices(request) -> dict:
1354+
async def get_available_voices(request) -> dict:
13431355
"""Returns {voice_id: voice_name} dict"""
13441356
available_voices = {}
13451357
if request.app.state.config.TTS_ENGINE == 'openai':
13461358
# Use custom endpoint if not using the official OpenAI API URL
13471359
if not request.app.state.config.TTS_OPENAI_API_BASE_URL.startswith('https://api.openai.com'):
13481360
try:
1349-
response = requests.get(
1350-
f'{request.app.state.config.TTS_OPENAI_API_BASE_URL}/audio/voices',
1351-
timeout=AIOHTTP_CLIENT_TIMEOUT_MODEL_LIST,
1352-
)
1353-
response.raise_for_status()
1354-
data = response.json()
1355-
voices_list = data.get('voices', [])
1356-
available_voices = {voice['id']: voice['name'] for voice in voices_list}
1361+
timeout = aiohttp.ClientTimeout(total=AIOHTTP_CLIENT_TIMEOUT_MODEL_LIST)
1362+
async with aiohttp.ClientSession(timeout=timeout, trust_env=True) as session:
1363+
async with session.get(
1364+
f'{request.app.state.config.TTS_OPENAI_API_BASE_URL}/audio/voices',
1365+
) as response:
1366+
response.raise_for_status()
1367+
data = await response.json()
1368+
voices_list = data.get('voices', [])
1369+
available_voices = {voice['id']: voice['name'] for voice in voices_list}
13571370
except Exception as e:
13581371
log.error(f'Error fetching voices from custom endpoint: {str(e)}')
13591372
available_voices = {
@@ -1375,7 +1388,7 @@ def get_available_voices(request) -> dict:
13751388
}
13761389
elif request.app.state.config.TTS_ENGINE == 'elevenlabs':
13771390
try:
1378-
available_voices = get_elevenlabs_voices(api_key=request.app.state.config.TTS_API_KEY)
1391+
available_voices = await get_elevenlabs_voices(api_key=request.app.state.config.TTS_API_KEY)
13791392
except Exception:
13801393
# Avoided @lru_cache with exception
13811394
pass
@@ -1386,43 +1399,45 @@ def get_available_voices(request) -> dict:
13861399
url = (base_url or f'https://{region}.tts.speech.microsoft.com') + '/cognitiveservices/voices/list'
13871400
headers = {'Ocp-Apim-Subscription-Key': request.app.state.config.TTS_API_KEY}
13881401

1389-
response = requests.get(url, headers=headers, timeout=AIOHTTP_CLIENT_TIMEOUT_MODEL_LIST)
1390-
response.raise_for_status()
1391-
voices = response.json()
1402+
timeout = aiohttp.ClientTimeout(total=AIOHTTP_CLIENT_TIMEOUT_MODEL_LIST)
1403+
async with aiohttp.ClientSession(timeout=timeout, trust_env=True) as session:
1404+
async with session.get(url, headers=headers) as response:
1405+
response.raise_for_status()
1406+
voices = await response.json()
13921407

1393-
for voice in voices:
1394-
available_voices[voice['ShortName']] = f'{voice["DisplayName"]} ({voice["ShortName"]})'
1395-
except requests.RequestException as e:
1408+
for voice in voices:
1409+
available_voices[voice['ShortName']] = f'{voice["DisplayName"]} ({voice["ShortName"]})'
1410+
except Exception as e:
13961411
log.error(f'Error fetching voices: {str(e)}')
13971412
elif request.app.state.config.TTS_ENGINE == 'mistral':
13981413
api_key = request.app.state.config.TTS_MISTRAL_API_KEY
13991414
api_base_url = request.app.state.config.TTS_MISTRAL_API_BASE_URL or 'https://api.mistral.ai/v1'
14001415

14011416
if api_key:
14021417
try:
1403-
response = requests.get(
1404-
f'{api_base_url}/audio/voices',
1405-
headers={
1406-
'Authorization': f'Bearer {api_key}',
1407-
},
1408-
timeout=AIOHTTP_CLIENT_TIMEOUT_MODEL_LIST,
1409-
)
1410-
response.raise_for_status()
1411-
voices_data = response.json()
1412-
1413-
for voice in voices_data:
1414-
voice_id = voice.get('voice_id', voice.get('id', ''))
1415-
voice_name = voice.get('name', voice_id)
1416-
if voice_id:
1417-
available_voices[voice_id] = voice_name
1418-
except requests.RequestException as e:
1418+
timeout = aiohttp.ClientTimeout(total=AIOHTTP_CLIENT_TIMEOUT_MODEL_LIST)
1419+
async with aiohttp.ClientSession(timeout=timeout, trust_env=True) as session:
1420+
async with session.get(
1421+
f'{api_base_url}/audio/voices',
1422+
headers={
1423+
'Authorization': f'Bearer {api_key}',
1424+
},
1425+
) as response:
1426+
response.raise_for_status()
1427+
voices_data = await response.json()
1428+
1429+
for voice in voices_data:
1430+
voice_id = voice.get('voice_id', voice.get('id', ''))
1431+
voice_name = voice.get('name', voice_id)
1432+
if voice_id:
1433+
available_voices[voice_id] = voice_name
1434+
except Exception as e:
14191435
log.error(f'Error fetching Mistral voices: {str(e)}')
14201436

14211437
return available_voices
14221438

14231439

1424-
@lru_cache
1425-
def get_elevenlabs_voices(api_key: str) -> dict:
1440+
async def get_elevenlabs_voices(api_key: str) -> dict:
14261441
"""
14271442
Note, set the following in your .env file to use Elevenlabs:
14281443
AUDIO_TTS_ENGINE=elevenlabs
@@ -1433,22 +1448,22 @@ def get_elevenlabs_voices(api_key: str) -> dict:
14331448

14341449
try:
14351450
# TODO: Add retries
1436-
response = requests.get(
1437-
f'{ELEVENLABS_API_BASE_URL}/v1/voices',
1438-
headers={
1439-
'xi-api-key': api_key,
1440-
'Content-Type': 'application/json',
1441-
},
1442-
timeout=AIOHTTP_CLIENT_TIMEOUT_MODEL_LIST,
1443-
)
1444-
response.raise_for_status()
1445-
voices_data = response.json()
1446-
1447-
voices = {}
1448-
for voice in voices_data.get('voices', []):
1449-
voices[voice['voice_id']] = voice['name']
1450-
except requests.RequestException as e:
1451-
# Avoid @lru_cache with exception
1451+
timeout = aiohttp.ClientTimeout(total=AIOHTTP_CLIENT_TIMEOUT_MODEL_LIST)
1452+
async with aiohttp.ClientSession(timeout=timeout, trust_env=True) as session:
1453+
async with session.get(
1454+
f'{ELEVENLABS_API_BASE_URL}/v1/voices',
1455+
headers={
1456+
'xi-api-key': api_key,
1457+
'Content-Type': 'application/json',
1458+
},
1459+
) as response:
1460+
response.raise_for_status()
1461+
voices_data = await response.json()
1462+
1463+
voices = {}
1464+
for voice in voices_data.get('voices', []):
1465+
voices[voice['voice_id']] = voice['name']
1466+
except Exception as e:
14521467
log.error(f'Error fetching voices: {str(e)}')
14531468
raise RuntimeError(f'Error fetching voices: {str(e)}')
14541469

@@ -1457,4 +1472,4 @@ def get_elevenlabs_voices(api_key: str) -> dict:
14571472

14581473
@router.get('/voices')
14591474
async def get_voices(request: Request, user=Depends(get_verified_user)):
1460-
return {'voices': [{'id': k, 'name': v} for k, v in get_available_voices(request).items()]}
1475+
return {'voices': [{'id': k, 'name': v} for k, v in (await get_available_voices(request)).items()]}

0 commit comments

Comments
 (0)