diff --git a/tools/tts_tool.py b/tools/tts_tool.py index 4d27cced65..37a8a3a26d 100644 --- a/tools/tts_tool.py +++ b/tools/tts_tool.py @@ -477,7 +477,10 @@ def _minimax_requirements() -> bool: def _xai_requirements() -> bool: try: from tools.xai_http import resolve_xai_http_credentials - return bool(resolve_xai_http_credentials().get("api_key")) + # Same ordering as _generate_xai_tts / XAIStreamer: an explicit key wins over the + # subscription OAuth bearer (which 403s on metered /v1/tts) — never touch the OAuth + # pool for an availability probe when a key is configured. See #87045, #113727. + return bool(resolve_xai_http_credentials(prefer_api_key=True).get("api_key")) except Exception: return False diff --git a/website/docs/developer-guide/streaming-tts.md b/website/docs/developer-guide/streaming-tts.md index 72981dff27..2cd77224e1 100644 --- a/website/docs/developer-guide/streaming-tts.md +++ b/website/docs/developer-guide/streaming-tts.md @@ -61,7 +61,7 @@ tts: | elevenlabs | chunked HTTP (`pcm_24000`) | yes | `ELEVENLABS_API_KEY` / `tts.elevenlabs` | | openai | chunked HTTP (`with_streaming_response`, `pcm`) | yes | `tts.openai.api_key` → env → managed gateway | | gemini | SSE (`streamGenerateContent?alt=sse`) | yes | `GEMINI_API_KEY` / `GOOGLE_API_KEY` | -| xai | WebSocket (`wss://api.x.ai/v1/tts`) | yes | xAI OAuth or `XAI_API_KEY` | +| xai | WebSocket (`wss://api.x.ai/v1/tts`) | yes | `XAI_API_KEY` preferred, else xAI OAuth (the subscription bearer 403s on metered TTS) | | edge, piper, kitten, neutts, mistral, minimax, deepinfra, … | — | no (per-sentence sync fallback) | as usual | All credential lookups go through `resolve_provider_secret()`