fix(tts): xAI TTS availability probe prefers XAI_API_KEY like the synthesis paths

`tools/tts_tool.py::_xai_requirements` (the provider check text_to_speech_tool
dispatch consults) still resolved xAI credentials OAuth-first, while both TTS
synthesis paths now pass `prefer_api_key=True`. Truthiness is unchanged, but a
configured key no longer routes the availability probe through the OAuth pool
(pool select / refresh) for a bearer the metered `/v1/tts` endpoint rejects.

Docs: the streaming-TTS provider table now states the ordering.

Part of the #113727 salvage of #113728 (@beardthelion); sibling site the PR
missed.

Co-authored-by: beardthelion <beardthelion@users.noreply.github.com>
This commit is contained in:
teknium1
2026-09-18 00:42:06 -07:00
committed by Teknium
parent 3b0aae77ee
commit d7865939af
2 changed files with 5 additions and 2 deletions

View File

@@ -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

View File

@@ -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()`