An OpenAI-SDK-shaped transcription response can be a structured object whose
``text`` is None and whose ``error`` carries the provider's failure. Every
caller fell back to ``str(transcription)``, so the object repr —
``Transcription(text=None, logprobs=None, usage=None, error='Transcription failed')``
— was logged as a successful transcript and returned in the
``{"success": true, "transcript": ...}`` envelope. Desktop conversation mode
then injected that repr as the user's message instead of the audio.
``_extract_transcript_text`` now raises ``STTResponseError`` (a ``ValueError``)
for any structured response — SDK object or JSON dict — with a missing or
non-string ``text``: the provider's ``error`` when there is one, else
"Transcription response contained no text". ``_with_openai_client`` and
``_cloud_failure`` surface that message verbatim, so the openai, groq,
deepinfra, mistral, xAI and ElevenLabs paths all return their existing failure
envelope instead. ``_transcribe_groq`` uses the shared normalizer rather than
its own ``str(transcription)``. Plain strings, objects/dicts with a string
``text`` (including ``""``, so silence stays non-fatal) and unknown scalars are
unchanged; only the repr fallback for structured responses is gone. No desktop
change is required.
Fixes#78098