diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 3f9f9a572c0..c8e1c19061a 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -10893,7 +10893,7 @@ async def elevenlabs_tts_stream_input_endpoint( similarity_boost: float | None = fastapi.Query(None, description="Voice similarity boost (0-1)."), style: float | None = fastapi.Query(None, description="Voice style (0-1, v2+ models only)."), speed: float | None = fastapi.Query(None, description="Speaking speed (0.7-1.2)."), - user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth_websocket), + user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth_websocket), # noqa: B008 # FastAPI Depends() is required in WebSocket endpoint signatures ): """ ElevenLabs WebSocket streaming-input TTS endpoint. @@ -10905,7 +10905,7 @@ async def elevenlabs_tts_stream_input_endpoint( Query parameters map directly to ElevenLabs voice/model options; no per-message auth is needed because the proxy injects xi-api-key upstream. """ - from datetime import datetime + from datetime import datetime, timezone from uuid import uuid4 import httpx @@ -10949,9 +10949,9 @@ async def elevenlabs_tts_stream_input_endpoint( }.items() if v is not None } - voice_settings: Final[VoiceSettings | None] = cast(VoiceSettings, raw_voice_settings) if raw_voice_settings else None + voice_settings: Final[VoiceSettings | None] = cast(VoiceSettings, raw_voice_settings) if raw_voice_settings else None # cast-ok: dict built from typed float query params; structural match is guaranteed - start_time: Final = datetime.now() + start_time: Final = datetime.now(tz=timezone.utc) litellm_call_id: Final = str(uuid4()) logging_obj: Final = Logging( @@ -10973,14 +10973,14 @@ async def elevenlabs_tts_stream_input_endpoint( voice_settings=voice_settings, ) - end_time: Final = datetime.now() + end_time: Final = datetime.now(tz=timezone.utc) try: model_info: Final = litellm.get_model_info( model=litellm_model, custom_llm_provider="elevenlabs" ) cost_per_char: Final = model_info.get("input_cost_per_character") or 0.0 - except Exception: + except Exception: # noqa: BLE001 # litellm.get_model_info raises multiple undocumented error types cost_per_char = 0.0 response_cost: Final = total_chars * cost_per_char @@ -10998,12 +10998,12 @@ async def elevenlabs_tts_stream_input_endpoint( end_time=end_time, cache_hit=False, ) - except Exception: + except Exception: # noqa: BLE001 # intentional: catch all session errors to ensure WS cleanup verbose_proxy_logger.exception("ElevenLabs TTS stream-input error") try: await websocket.close(code=1011, reason="Internal server error") - except Exception: - pass + except Exception: # noqa: BLE001 # WS may already be closed; log and discard + verbose_proxy_logger.debug("WebSocket already closed during error cleanup") ###################################################################### diff --git a/ui/litellm-dashboard/src/lib/http/schema.d.ts b/ui/litellm-dashboard/src/lib/http/schema.d.ts index cf37709c377..63ddc43ba66 100644 --- a/ui/litellm-dashboard/src/lib/http/schema.d.ts +++ b/ui/litellm-dashboard/src/lib/http/schema.d.ts @@ -684,6 +684,26 @@ export interface paths { patch?: never; trace?: never; }; + "/audio/speech/stream-input": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * WebSocket: elevenlabs_tts_stream_input_endpoint + * @description WebSocket connection endpoint + */ + get: operations["websocket_elevenlabs_tts_stream_input_endpoint_get"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/audio/transcriptions": { parameters: { query?: never; @@ -16062,6 +16082,26 @@ export interface paths { patch?: never; trace?: never; }; + "/v1/audio/speech/stream-input": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * WebSocket: elevenlabs_tts_stream_input_endpoint + * @description WebSocket connection endpoint + */ + get: operations["websocket_elevenlabs_tts_stream_input_endpoint_get_2"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/v1/audio/transcriptions": { parameters: { query?: never; @@ -37168,6 +37208,24 @@ export interface operations { }; }; }; + websocket_elevenlabs_tts_stream_input_endpoint_get: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description WebSocket Protocol Switched */ + 101: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + }; + }; audio_transcriptions_audio_transcriptions_post: { parameters: { query?: never; @@ -55789,6 +55847,24 @@ export interface operations { }; }; }; + websocket_elevenlabs_tts_stream_input_endpoint_get_2: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description WebSocket Protocol Switched */ + 101: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + }; + }; audio_transcriptions_v1_audio_transcriptions_post: { parameters: { query?: never;